@pi-unipi/unipi 3.0.0-alpha.22 → 3.0.0-alpha.23
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 +21 -0
- package/README.md +12 -13
- package/package.json +22 -22
- package/packages/ask-user/config.ts +12 -12
- package/packages/ask-user/package.json +2 -2
- package/packages/autocomplete/package.json +1 -1
- package/packages/autocomplete/src/settings.ts +1 -1
- package/packages/background-tasks/package.json +2 -2
- package/packages/background-tasks/src/config.ts +5 -18
- package/packages/btw/package.json +2 -2
- package/packages/compactor/package.json +3 -3
- package/packages/compactor/src/compaction/hooks.ts +3 -3
- package/packages/compactor/src/config/manager.ts +85 -30
- package/packages/compactor/src/config/schema.ts +20 -5
- package/packages/compactor/src/types.ts +2 -6
- package/packages/core/package.json +1 -1
- package/packages/core/src/hints/index.ts +3 -3
- package/packages/core/src/jev/settings.ts +11 -10
- package/packages/core/src/settings/hub.ts +133 -1
- package/packages/core/src/settings/schema.ts +24 -5
- package/packages/core/src/tui/hub-kit.ts +10 -1
- package/packages/footer/package.json +3 -3
- package/packages/footer/src/config.ts +39 -19
- package/packages/fusion/package.json +4 -4
- package/packages/fusion/src/preset.ts +5 -5
- package/packages/info-screen/config.ts +11 -6
- package/packages/info-screen/package.json +2 -2
- package/packages/input-shortcuts/package.json +2 -2
- package/packages/input-shortcuts/src/settings.ts +4 -0
- package/packages/kanboard/package.json +7 -7
- package/packages/kanboard/src/settings.ts +36 -30
- package/packages/long-horizon/package.json +2 -2
- package/packages/long-horizon/src/settings.ts +16 -10
- package/packages/mcp/package.json +2 -2
- package/packages/mcp/src/index.ts +1 -42
- package/packages/mcp/src/settings.ts +52 -0
- package/packages/memory/index.ts +2 -1
- package/packages/memory/package.json +3 -3
- package/packages/memory/settings.ts +103 -26
- package/packages/notify/package.json +2 -2
- package/packages/notify/settings.ts +58 -26
- package/packages/skill-registry/package.json +2 -2
- package/packages/skill-registry/src/settings.ts +9 -9
- package/packages/subagents/package.json +3 -3
- package/packages/subagents/src/index.ts +2 -1
- package/packages/unipi/bundled.js +732 -402
- package/packages/updater/package.json +2 -2
- package/packages/updater/src/settings.ts +8 -2
- package/packages/utility/package.json +2 -2
- package/packages/utility/src/image/settings.ts +74 -22
- package/packages/utility/src/settings.ts +16 -16
- package/packages/watchdog/package.json +3 -3
- package/packages/watchdog/src/config.ts +14 -13
- package/packages/web-api/package.json +2 -2
- package/packages/web-api/src/index.ts +17 -1
- package/packages/web-api/src/settings.ts +27 -14
- package/packages/web-api/src/tools.ts +50 -40
- package/packages/workflow/package.json +2 -2
- package/packages/workflow/src/permission/decide.ts +2 -3
- package/packages/workflow/src/permission/index.ts +0 -1
- package/packages/workflow/src/permission/settings.ts +10 -19
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [3.0.0-alpha.23] — 2026-10-04
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `core`: the `/unipi:settings` hub explains the selected setting in a fixed 4-row info panel — wrapped description, the highlighted (or stored) enum option's meaning, and a meta line (`default … · range … · scope`). Enum and multiselect options carry their own descriptions, shown while the option list is open; number fields take `unit` (`6000 ms`, `80%`); option descriptions join the search haystack. (UNI-50)
|
|
13
|
+
- guard test (`tests/settings-metadata.test.ts`): every settings field and option must be described (labels ≤ 24 chars, sentences end with a period, units live in `unit`, option fragments don't) across all 23 namespaces.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- `web-api`: web tools that cannot work are no longer exposed to the agent — `web_search`/`web_llm_summarize` drop out of the active tools when no enabled provider can serve them (re-checked every prompt), `multi_web_content_read` always stays (smart-fetch is local). The phantom `llm-summarize` provider is gone and `web_llm_summarize` lost its useless `source` parameter. Tool failures now throw instead of returning ignored `isError` results. (UNI-72)
|
|
17
|
+
- settings: shorter labels (explanations moved into descriptions), and pruned knobs —
|
|
18
|
+
- `background-tasks`: removed `defaultTimeoutSeconds` (nothing read it).
|
|
19
|
+
- `compactor`: the nine `sections.*` booleans became one **Summary sections** multiselect (stored booleans still work); `cooldownMs`/`repeatMinGrowthTokens` fixed at their defaults; `debug` moved to `UNIPI_COMPACTOR_DEBUG=1`.
|
|
20
|
+
- `memory`: `recallAtStart` + `wakeUp` merged into one **Recall at start** enum (`off`/`reminder`/`wake-up`); stored booleans migrate per scope.
|
|
21
|
+
- `permission`: removed `jevJudge` — auto mode always consults jev.
|
|
22
|
+
- `footer`: classic-only options (`preset`, `separator`, `zoneSeparator`, `full labels`, `color mode`) moved under an advanced **Classic footer** section; preset list now offers the real presets (`compact`, `full`, `ascii` instead of the dead `dense`/`devops`/`zen`).
|
|
23
|
+
- `image`: custom endpoint fields moved into per-tool **Custom endpoint…** pages (summary shows `off` or the host).
|
|
24
|
+
- `web-api`: Smart fetch section is advanced; `os`/`batchConcurrency` removed from the schema (engine defaults remain).
|
|
25
|
+
- `kanboard`: reorganized into Agent / Housekeeping / **Board server** (advanced, plus open/stop/rotate-token actions); dead `blocking` setting deleted.
|
|
26
|
+
|
|
27
|
+
### Docs
|
|
28
|
+
- README: dropped the "Why we have it" column from the Proof table.
|
|
29
|
+
|
|
9
30
|
## [3.0.0-alpha.22] — 2026-10-03
|
|
10
31
|
|
|
11
32
|
### Changed
|
package/README.md
CHANGED
|
@@ -68,19 +68,18 @@ runs commands. UniPi adds the parts that a long session needs.
|
|
|
68
68
|
## Proof
|
|
69
69
|
|
|
70
70
|
Each feature below exists because published results support it. The numbers
|
|
71
|
-
come from the linked sources. They are not UniPi benchmarks.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
| **
|
|
77
|
-
| **Fusion
|
|
78
|
-
| **
|
|
79
|
-
| **
|
|
80
|
-
| **
|
|
81
|
-
| **
|
|
82
|
-
| **
|
|
83
|
-
| **Prefix cache** | Up to 90% lower cost and 85% lower latency for long prompts. | Each agent turn sends the full conversation again. In our UniPi 2.4 logs, each cache miss sent about 62,700 tokens again at the full price. UniPi keeps the prefix byte-identical, so a turn pays full price only for the new tail. | [Anthropic prompt caching](https://claude.com/blog/prompt-caching) · [Modular handbook](https://handbook.modular.com/inference-optimization/prefix-caching/) · [our cache study](docs/deepseek-cache-rate-research.md) |
|
|
71
|
+
come from the linked sources. They are not UniPi benchmarks.
|
|
72
|
+
|
|
73
|
+
| Feature | Benchmark | Source |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| **Memory** | 96.6% recall@5 on LongMemEval, with raw semantic search and 0 API calls. | [MemPalace](https://github.com/MemPalace/mempalace) · [LongMemEval](https://arxiv.org/abs/2410.10813) |
|
|
76
|
+
| **Fusion** | Frontier-level FrontierCode score at up to 60% lower cost. | [Devin Fusion](https://cognition.com/blog/devin-fusion) |
|
|
77
|
+
| **Fusion with two frontier models** | 69.0% on DRACO for two frontier models together. The best single model scored 65.3%. | [OpenRouter Fusion](https://openrouter.ai/blog/announcements/fusion-beats-frontier/) · [Mixture-of-Agents](https://arxiv.org/abs/2406.04692) |
|
|
78
|
+
| **Goal** | HumanEval pass@1 went from 80% to 91% with a loop on test feedback. | [Reflexion](https://arxiv.org/abs/2303.11366) · [Self-authored verification](https://arxiv.org/abs/2607.24300) |
|
|
79
|
+
| **Ralph** | Six repositories overnight for $297 in API cost (a reported hackathon result). | [Ralph loop](https://ghuntley.com/ralph/) · [YC hackathon report](https://paddo.dev/blog/ralph-wiggum-autonomous-loops/) |
|
|
80
|
+
| **Swarm** | 90.2% higher than a single agent on Anthropic's research eval. | [Anthropic multi-agent research](https://www.anthropic.com/engineering/multi-agent-research-system) |
|
|
81
|
+
| **Graph** | Up to 3.7× lower latency, 6.7× lower cost and about 9% higher accuracy than ReAct. | [LLMCompiler (ICML 2024)](https://arxiv.org/abs/2312.04511) |
|
|
82
|
+
| **Prefix cache** | Up to 90% lower cost and 85% lower latency for long prompts. | [Anthropic prompt caching](https://claude.com/blog/prompt-caching) · [Modular handbook](https://handbook.modular.com/inference-optimization/prefix-caching/) · [our cache study](docs/deepseek-cache-rate-research.md) |
|
|
84
83
|
|
|
85
84
|
Long work matters more each year. METR measured that the length of software
|
|
86
85
|
tasks that agents can finish doubles about every 7 months
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/unipi",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.23",
|
|
4
4
|
"description": "All-in-one extension suite for Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"prepare": "node scripts/build-bundle.mjs --if-missing",
|
|
28
28
|
"prepublishOnly": "npm run build",
|
|
29
29
|
"publish:all": "npm publish --workspaces --access public",
|
|
30
|
-
"test": "npx tsx --test tests/*.test.js packages/memory/tests/*.test.ts && npm test --workspaces --if-present",
|
|
30
|
+
"test": "npx tsx --test tests/*.test.js tests/*.test.ts packages/memory/tests/*.test.ts && npm test --workspaces --if-present",
|
|
31
31
|
"typecheck": "npx tsc --noEmit --skipLibCheck",
|
|
32
32
|
"tui:gallery": "npx tsx scripts/tui-gallery/index.ts",
|
|
33
33
|
"publish:kanboard": "node scripts/publish-kanboard-platforms.mjs"
|
|
@@ -82,26 +82,26 @@
|
|
|
82
82
|
"typebox": "^1.1.38"
|
|
83
83
|
},
|
|
84
84
|
"dependencies": {
|
|
85
|
-
"@pi-unipi/ask-user": "3.0.0-alpha.
|
|
86
|
-
"@pi-unipi/background-tasks": "3.0.0-alpha.
|
|
87
|
-
"@pi-unipi/btw": "3.0.0-alpha.
|
|
88
|
-
"@pi-unipi/command-enchantment": "3.0.0-alpha.
|
|
89
|
-
"@pi-unipi/compactor": "3.0.0-alpha.
|
|
90
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
91
|
-
"@pi-unipi/footer": "3.0.0-alpha.
|
|
92
|
-
"@pi-unipi/fusion": "3.0.0-alpha.
|
|
93
|
-
"@pi-unipi/info-screen": "3.0.0-alpha.
|
|
94
|
-
"@pi-unipi/input-shortcuts": "3.0.0-alpha.
|
|
95
|
-
"@pi-unipi/kanboard": "3.0.0-alpha.
|
|
96
|
-
"@pi-unipi/mcp": "3.0.0-alpha.
|
|
97
|
-
"@pi-unipi/memory": "3.0.0-alpha.
|
|
98
|
-
"@pi-unipi/notify": "3.0.0-alpha.
|
|
99
|
-
"@pi-unipi/skill-registry": "3.0.0-alpha.
|
|
100
|
-
"@pi-unipi/subagents": "3.0.0-alpha.
|
|
101
|
-
"@pi-unipi/updater": "3.0.0-alpha.
|
|
102
|
-
"@pi-unipi/utility": "3.0.0-alpha.
|
|
103
|
-
"@pi-unipi/web-api": "3.0.0-alpha.
|
|
104
|
-
"@pi-unipi/workflow": "3.0.0-alpha.
|
|
85
|
+
"@pi-unipi/ask-user": "3.0.0-alpha.23",
|
|
86
|
+
"@pi-unipi/background-tasks": "3.0.0-alpha.23",
|
|
87
|
+
"@pi-unipi/btw": "3.0.0-alpha.23",
|
|
88
|
+
"@pi-unipi/command-enchantment": "3.0.0-alpha.23",
|
|
89
|
+
"@pi-unipi/compactor": "3.0.0-alpha.23",
|
|
90
|
+
"@pi-unipi/core": "3.0.0-alpha.23",
|
|
91
|
+
"@pi-unipi/footer": "3.0.0-alpha.23",
|
|
92
|
+
"@pi-unipi/fusion": "3.0.0-alpha.23",
|
|
93
|
+
"@pi-unipi/info-screen": "3.0.0-alpha.23",
|
|
94
|
+
"@pi-unipi/input-shortcuts": "3.0.0-alpha.23",
|
|
95
|
+
"@pi-unipi/kanboard": "3.0.0-alpha.23",
|
|
96
|
+
"@pi-unipi/mcp": "3.0.0-alpha.23",
|
|
97
|
+
"@pi-unipi/memory": "3.0.0-alpha.23",
|
|
98
|
+
"@pi-unipi/notify": "3.0.0-alpha.23",
|
|
99
|
+
"@pi-unipi/skill-registry": "3.0.0-alpha.23",
|
|
100
|
+
"@pi-unipi/subagents": "3.0.0-alpha.23",
|
|
101
|
+
"@pi-unipi/updater": "3.0.0-alpha.23",
|
|
102
|
+
"@pi-unipi/utility": "3.0.0-alpha.23",
|
|
103
|
+
"@pi-unipi/web-api": "3.0.0-alpha.23",
|
|
104
|
+
"@pi-unipi/workflow": "3.0.0-alpha.23"
|
|
105
105
|
},
|
|
106
106
|
"devDependencies": {
|
|
107
107
|
"@earendil-works/pi-agent-core": "^0.87.1",
|
|
@@ -44,9 +44,9 @@ registerSettings({
|
|
|
44
44
|
{
|
|
45
45
|
title: "Tool",
|
|
46
46
|
fields: [
|
|
47
|
-
{ key: "enabled", type: "boolean", label: "Enable ask_user", description: "Let the agent stop and ask you multiple-choice questions" },
|
|
48
|
-
{ key: "notifyOnAsk", type: "boolean", label: "Notify when asked", description: "Send a notification when the agent is waiting for your answer" },
|
|
49
|
-
{ key: "maxQuestions", type: "number", label: "Questions per call", min: 1, max: 4, description: "How many questions one dialog may hold
|
|
47
|
+
{ key: "enabled", type: "boolean", label: "Enable ask_user", description: "Let the agent stop and ask you multiple-choice questions." },
|
|
48
|
+
{ key: "notifyOnAsk", type: "boolean", label: "Notify when asked", description: "Send a notification when the agent is waiting for your answer." },
|
|
49
|
+
{ key: "maxQuestions", type: "number", label: "Questions per call", min: 1, max: 4, description: "How many questions one dialog may hold. Applies to new sessions." },
|
|
50
50
|
],
|
|
51
51
|
},
|
|
52
52
|
{
|
|
@@ -58,24 +58,24 @@ registerSettings({
|
|
|
58
58
|
type: "enum",
|
|
59
59
|
label: "Esc",
|
|
60
60
|
options: [
|
|
61
|
-
{ value: "stop", label: "stop the agent's turn" },
|
|
62
|
-
{ value: "send", label: "send what's answered
|
|
61
|
+
{ value: "stop", label: "stop", description: "stop the agent's turn" },
|
|
62
|
+
{ value: "send", label: "send", description: "send what's answered, skip the rest" },
|
|
63
63
|
],
|
|
64
|
-
description: "What Esc does in the dialog",
|
|
64
|
+
description: "What Esc does in the dialog.",
|
|
65
65
|
},
|
|
66
|
-
{ key: "digitAdvance", type: "boolean", label: "Number keys move on", description: "In a single-choice question, picking with 1–9 also
|
|
66
|
+
{ key: "digitAdvance", type: "boolean", label: "Number keys move on", description: "In a single-choice question, picking with 1–9 also advances." },
|
|
67
67
|
{
|
|
68
68
|
key: "other",
|
|
69
69
|
type: "enum",
|
|
70
70
|
label: "\"Other\" choice",
|
|
71
71
|
options: [
|
|
72
|
-
{ value: "agent", label: "agent decides
|
|
73
|
-
{ value: "always", label: "always offer
|
|
74
|
-
{ value: "never", label: "never offer
|
|
72
|
+
{ value: "agent", label: "agent decides", description: "on unless the agent opts out" },
|
|
73
|
+
{ value: "always", label: "always", description: "always offer the Other row" },
|
|
74
|
+
{ value: "never", label: "never", description: "never offer the Other row" },
|
|
75
75
|
],
|
|
76
|
-
description: "The free-text \"Other (type your own)\" row",
|
|
76
|
+
description: "The free-text \"Other (type your own)\" row.",
|
|
77
77
|
},
|
|
78
|
-
{ key: "helpLine", type: "boolean", label: "\"Not ready\" line", description: "Show \"? Not ready to answer, help me out!\" under the dialog" },
|
|
78
|
+
{ key: "helpLine", type: "boolean", label: "\"Not ready\" line", description: "Show \"? Not ready to answer, help me out!\" under the dialog." },
|
|
79
79
|
],
|
|
80
80
|
},
|
|
81
81
|
],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/ask-user",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.23",
|
|
4
4
|
"description": "Structured user input tool for Pi coding agent — single-select, multi-select, freeform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
43
|
+
"@pi-unipi/core": "3.0.0-alpha.23"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"@earendil-works/pi-coding-agent": "^0.87.1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/background-tasks",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.23",
|
|
4
4
|
"description": "Background tasks for UniPi — durable shell jobs and delegated background agents with a footer dock",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@earendil-works/pi-ai": "^0.87.1",
|
|
13
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
13
|
+
"@pi-unipi/core": "3.0.0-alpha.23",
|
|
14
14
|
"turndown": "^7.2.4"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
@@ -21,8 +21,6 @@ export interface BackgroundTasksConfig {
|
|
|
21
21
|
notifyOnCompletion: boolean;
|
|
22
22
|
/** Default triggerOnCompletion (follow-up wake) for bg_run tasks. */
|
|
23
23
|
triggerOnCompletion: boolean;
|
|
24
|
-
/** Default timeoutSeconds for shell tasks (0 = none). */
|
|
25
|
-
defaultTimeoutSeconds: number;
|
|
26
24
|
/** Max finished tasks retained in memory. */
|
|
27
25
|
maxFinishedTasks: number;
|
|
28
26
|
/** Output cap in bytes before a task is killed+failed (default 20 MiB). */
|
|
@@ -33,7 +31,6 @@ export const DEFAULT_CONFIG: BackgroundTasksConfig = {
|
|
|
33
31
|
enabled: true,
|
|
34
32
|
notifyOnCompletion: true,
|
|
35
33
|
triggerOnCompletion: true,
|
|
36
|
-
defaultTimeoutSeconds: 0,
|
|
37
34
|
maxFinishedTasks: 30,
|
|
38
35
|
maxOutputBytes: 20 * 1024 * 1024,
|
|
39
36
|
};
|
|
@@ -114,11 +111,6 @@ export function validateBackgroundTasksConfig(config: unknown): string[] {
|
|
|
114
111
|
problems.push(`${key} must be a boolean`);
|
|
115
112
|
}
|
|
116
113
|
}
|
|
117
|
-
if (c.defaultTimeoutSeconds !== undefined) {
|
|
118
|
-
if (typeof c.defaultTimeoutSeconds !== "number" || !Number.isFinite(c.defaultTimeoutSeconds) || c.defaultTimeoutSeconds < 0) {
|
|
119
|
-
problems.push("defaultTimeoutSeconds must be a non-negative number");
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
114
|
if (c.maxFinishedTasks !== undefined) {
|
|
123
115
|
if (typeof c.maxFinishedTasks !== "number" || !Number.isInteger(c.maxFinishedTasks) || c.maxFinishedTasks < 1) {
|
|
124
116
|
problems.push("maxFinishedTasks must be a positive integer");
|
|
@@ -154,12 +146,11 @@ registerSettings({
|
|
|
154
146
|
{
|
|
155
147
|
title: "Tasks",
|
|
156
148
|
fields: [
|
|
157
|
-
{ key: "enabled", type: "boolean", label: "Enabled", description: "Master toggle — off registers
|
|
158
|
-
{ key: "notifyOnCompletion", type: "boolean", label: "Notify on completion" },
|
|
159
|
-
{ key: "triggerOnCompletion", type: "boolean", label: "Follow-up wake", description: "
|
|
160
|
-
{ key: "
|
|
161
|
-
{ key: "
|
|
162
|
-
{ key: "maxOutputBytes", type: "number", label: "Output cap bytes", min: 1024, description: "Kill+fail a task past this output size" },
|
|
149
|
+
{ key: "enabled", type: "boolean", label: "Enabled", description: "Master toggle — off registers no tools, commands, hooks or UI." },
|
|
150
|
+
{ key: "notifyOnCompletion", type: "boolean", label: "Notify on completion", description: "Default for tool-launched tasks; bg_run can override per call." },
|
|
151
|
+
{ key: "triggerOnCompletion", type: "boolean", label: "Follow-up wake", description: "A finished task's terminal state wakes the agent for a follow-up turn." },
|
|
152
|
+
{ key: "maxFinishedTasks", type: "number", label: "Max finished kept", min: 1, description: "Finished tasks kept in memory before the oldest is dropped." },
|
|
153
|
+
{ key: "maxOutputBytes", type: "number", label: "Output cap", unit: "bytes", min: 1024, description: "Kill and fail a task that outputs more than this." },
|
|
163
154
|
],
|
|
164
155
|
},
|
|
165
156
|
],
|
|
@@ -202,10 +193,6 @@ export function loadBackgroundTasksConfig(cwd: string): LoadedBackgroundTasksCon
|
|
|
202
193
|
typeof merged.notifyOnCompletion === "boolean" ? merged.notifyOnCompletion : DEFAULT_CONFIG.notifyOnCompletion,
|
|
203
194
|
triggerOnCompletion:
|
|
204
195
|
typeof merged.triggerOnCompletion === "boolean" ? merged.triggerOnCompletion : DEFAULT_CONFIG.triggerOnCompletion,
|
|
205
|
-
defaultTimeoutSeconds:
|
|
206
|
-
typeof merged.defaultTimeoutSeconds === "number" && Number.isFinite(merged.defaultTimeoutSeconds) && merged.defaultTimeoutSeconds >= 0
|
|
207
|
-
? merged.defaultTimeoutSeconds
|
|
208
|
-
: DEFAULT_CONFIG.defaultTimeoutSeconds,
|
|
209
196
|
maxFinishedTasks:
|
|
210
197
|
typeof merged.maxFinishedTasks === "number" && Number.isInteger(merged.maxFinishedTasks) && merged.maxFinishedTasks >= 1
|
|
211
198
|
? merged.maxFinishedTasks
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/btw",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.23",
|
|
4
4
|
"description": "A pi extension for parallel side conversations with /unipi:btw — part of the Unipi suite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "extensions/btw.ts",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@earendil-works/pi-tui": "^0.87.1"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
40
|
+
"@pi-unipi/core": "3.0.0-alpha.23"
|
|
41
41
|
},
|
|
42
42
|
"pi": {
|
|
43
43
|
"extensions": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/compactor",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.23",
|
|
4
4
|
"description": "Compaction for Pi — lossless zero-LLM or model summaries, rebuilt from full history, with session recall",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
37
|
-
"@pi-unipi/info-screen": "3.0.0-alpha.
|
|
36
|
+
"@pi-unipi/core": "3.0.0-alpha.23",
|
|
37
|
+
"@pi-unipi/info-screen": "3.0.0-alpha.23",
|
|
38
38
|
"@earendil-works/pi-agent-core": "^0.87.1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
@@ -362,7 +362,7 @@ export function registerCompactionHooks(pi: ExtensionAPI, deps: CompactionHookDe
|
|
|
362
362
|
} catch (err) {
|
|
363
363
|
if (event.signal?.aborted) return { cancel: true };
|
|
364
364
|
// Fall back to Pi's own path (same summarizer, Pi's auth handling).
|
|
365
|
-
dbg(
|
|
365
|
+
dbg(process.env.UNIPI_COMPACTOR_DEBUG === "1", { method: "llm", fallback: true, error: String(err) });
|
|
366
366
|
return;
|
|
367
367
|
}
|
|
368
368
|
}
|
|
@@ -394,14 +394,14 @@ export function registerCompactionHooks(pi: ExtensionAPI, deps: CompactionHookDe
|
|
|
394
394
|
ctx?.ui?.notify?.(plan.reason === "no_live_messages" ? "compactor: nothing to compact" : "compactor: too few messages to compact", "warning");
|
|
395
395
|
} catch {}
|
|
396
396
|
}
|
|
397
|
-
dbg(
|
|
397
|
+
dbg(process.env.UNIPI_COMPACTOR_DEBUG === "1", { cancelled: true, reason: plan.reason });
|
|
398
398
|
return { cancel: true };
|
|
399
399
|
}
|
|
400
400
|
|
|
401
401
|
lastStats = plan.stats;
|
|
402
402
|
lastDetails = plan.details;
|
|
403
403
|
if (!parsed.isCompactor && parsed.followUpPrompt && reason === "manual") pendingFollowUpPrompt = parsed.followUpPrompt;
|
|
404
|
-
dbg(
|
|
404
|
+
dbg(process.env.UNIPI_COMPACTOR_DEBUG === "1", { method: "vcc", reason, stats: plan.stats, details: plan.details, summaryPreview: plan.summary.slice(0, 800) });
|
|
405
405
|
return {
|
|
406
406
|
compaction: {
|
|
407
407
|
summary: plan.summary,
|
|
@@ -6,11 +6,24 @@ import { homedir } from "node:os";
|
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { getSettingsScoped, registerSettings, setSettings } from "@pi-unipi/core";
|
|
8
8
|
import type { CompactorConfig } from "../types.js";
|
|
9
|
-
import { DEFAULT_COMPACTOR_CONFIG } from "./schema.js";
|
|
9
|
+
import { DEFAULT_COMPACTOR_CONFIG, SUMMARY_SECTION_IDS } from "./schema.js";
|
|
10
10
|
|
|
11
11
|
const METHOD_OPTIONS = [
|
|
12
|
-
{ value: "vcc", label: "lossless
|
|
13
|
-
{ value: "llm", label: "model summary" },
|
|
12
|
+
{ value: "vcc", label: "lossless", description: "instant structured summary; history stays searchable" },
|
|
13
|
+
{ value: "llm", label: "model summary", description: "pi's model writes the summary — costs one model call" },
|
|
14
|
+
] as const;
|
|
15
|
+
|
|
16
|
+
/** The `summarySections` multiselect: labels + explanations per section id. */
|
|
17
|
+
const SUMMARY_SECTION_OPTIONS = [
|
|
18
|
+
{ value: "activeWork", label: "Active work", description: "goal, ralph and kanboard state" },
|
|
19
|
+
{ value: "requests", label: "Your requests", description: "the user requests this session answered" },
|
|
20
|
+
{ value: "state", label: "Latest state", description: "the agent's most recent progress reports" },
|
|
21
|
+
{ value: "decisions", label: "Decisions", description: "decisions and constraints reached so far" },
|
|
22
|
+
{ value: "files", label: "Files", description: "files touched and what was done to them" },
|
|
23
|
+
{ value: "commits", label: "Commits", description: "commit history of this session" },
|
|
24
|
+
{ value: "errors", label: "Open errors", description: "unresolved errors worth remembering" },
|
|
25
|
+
{ value: "lessons", label: "Lessons", description: "memory notes and diagnoses written down" },
|
|
26
|
+
{ value: "transcript", label: "Recent transcript", description: "the last turns kept verbatim" },
|
|
14
27
|
] as const;
|
|
15
28
|
|
|
16
29
|
// Registered with the unified settings hub. Canonical paths:
|
|
@@ -29,28 +42,28 @@ registerSettings({
|
|
|
29
42
|
key: "method",
|
|
30
43
|
type: "enum",
|
|
31
44
|
label: "Method",
|
|
32
|
-
description: "
|
|
45
|
+
description: "How the context is summarized. Takes effect on the next compaction.",
|
|
33
46
|
options: METHOD_OPTIONS,
|
|
34
47
|
},
|
|
35
48
|
{
|
|
36
49
|
key: "piCompact",
|
|
37
50
|
type: "enum",
|
|
38
51
|
label: "Pi's /compact",
|
|
39
|
-
description: "What
|
|
40
|
-
options: [{ value: "follow", label: "same as Method" }, ...METHOD_OPTIONS],
|
|
52
|
+
description: "What pi's built-in /compact command does: follow Method, or force one.",
|
|
53
|
+
options: [{ value: "follow", label: "same as Method", description: "reuse whatever Method is set to" }, ...METHOD_OPTIONS],
|
|
41
54
|
},
|
|
42
55
|
{
|
|
43
56
|
key: "trigger",
|
|
44
57
|
type: "enum",
|
|
45
58
|
label: "When",
|
|
46
|
-
description: "
|
|
59
|
+
description: "What trips a compaction while you work.",
|
|
47
60
|
options: [
|
|
48
|
-
{ value: "pi", label: "
|
|
49
|
-
{ value: "percent", label: "at a percentage" },
|
|
61
|
+
{ value: "pi", label: "pi's context limit", description: "compact when pi itself nears the model's window" },
|
|
62
|
+
{ value: "percent", label: "at a percentage", description: "compact when usage crosses the threshold below" },
|
|
50
63
|
],
|
|
51
64
|
},
|
|
52
|
-
{ key: "thresholdPercent", type: "number", label: "
|
|
53
|
-
{ key: "notify", type: "boolean", label: "Notifications", description: "
|
|
65
|
+
{ key: "thresholdPercent", type: "number", label: "Trigger threshold", unit: "%", min: 30, max: 95, description: "Context share that trips a compaction when When = at a percentage." },
|
|
66
|
+
{ key: "notify", type: "boolean", label: "Notifications", description: "Notice when a compaction runs or fails." },
|
|
54
67
|
],
|
|
55
68
|
},
|
|
56
69
|
{
|
|
@@ -58,21 +71,17 @@ registerSettings({
|
|
|
58
71
|
description: "Tuned defaults — rarely worth changing",
|
|
59
72
|
advanced: true,
|
|
60
73
|
fields: [
|
|
61
|
-
{ key: "smartKeepTail", type: "boolean", label: "Smart keep tail", description: "Keep more recent turns when the kept tail would be tiny (≤5k tokens, up to 25k)" },
|
|
62
|
-
{ key: "summaryBudgetTokens", type: "number", label: "Summary budget", min: 0, max: 20000, zeroLabel: "auto", description: "Lossless summary size in tokens (auto scales 1.5k–4k with session size)" },
|
|
63
|
-
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
{ key: "
|
|
72
|
-
{ key: "cooldownMs", type: "number", label: "Percentage cooldown ms", min: 0, zeroLabel: "none", description: "Minimum delay between percentage-triggered compactions" },
|
|
73
|
-
{ key: "repeatMinGrowthTokens", type: "number", label: "Percentage repeat growth", min: 0, zeroLabel: "off", description: "New tokens needed to compact again while still above the percentage" },
|
|
74
|
-
{ key: "llmInstructions", type: "string", label: "Model summary instructions", emptyLabel: "none", description: "Extra instructions passed to model-written summaries" },
|
|
75
|
-
{ key: "debug", type: "boolean", label: "Debug output", description: "Write compaction diagnostics to /tmp/compactor-debug.json" },
|
|
74
|
+
{ key: "smartKeepTail", type: "boolean", label: "Smart keep tail", description: "Keep more recent turns when the kept tail would be tiny (≤5k tokens, up to 25k)." },
|
|
75
|
+
{ key: "summaryBudgetTokens", type: "number", label: "Summary budget", unit: "tokens", min: 0, max: 20000, zeroLabel: "auto", description: "Lossless summary size in tokens (auto scales 1.5k–4k with session size)." },
|
|
76
|
+
{
|
|
77
|
+
key: "summarySections",
|
|
78
|
+
type: "multiselect",
|
|
79
|
+
label: "Summary sections",
|
|
80
|
+
description: "What the summary keeps — deselect to drop it from every new summary.",
|
|
81
|
+
options: SUMMARY_SECTION_OPTIONS,
|
|
82
|
+
emptyLabel: "none",
|
|
83
|
+
},
|
|
84
|
+
{ key: "llmInstructions", type: "string", label: "Summary instructions", emptyLabel: "none", description: "Extra instructions passed to model-written summaries." },
|
|
76
85
|
],
|
|
77
86
|
},
|
|
78
87
|
],
|
|
@@ -103,8 +112,6 @@ export function translateLegacyConfig(raw: Raw): Raw {
|
|
|
103
112
|
if (!("trigger" in raw) && auto.enabled === true) out.trigger = "percent";
|
|
104
113
|
const carry: Array<[keyof CompactorConfig, string]> = [
|
|
105
114
|
["thresholdPercent", "thresholdPercent"],
|
|
106
|
-
["cooldownMs", "cooldownMs"],
|
|
107
|
-
["repeatMinGrowthTokens", "repeatMinGrowthTokens"],
|
|
108
115
|
["notify", "notify"],
|
|
109
116
|
];
|
|
110
117
|
for (const [to, from] of carry) {
|
|
@@ -137,9 +144,49 @@ export function loadConfig(cwd: string = process.cwd()): CompactorConfig {
|
|
|
137
144
|
}
|
|
138
145
|
if (config.method === "jev") config.method = "vcc";
|
|
139
146
|
if (config.piCompact === "jev") config.piCompact = "vcc";
|
|
147
|
+
// The defaults-merged object always carries summarySections, so the stored
|
|
148
|
+
// layers decide: new array wins, else legacy sections booleans, else default.
|
|
149
|
+
const selection = storedSummarySelection(cwd);
|
|
150
|
+
if (selection !== undefined) config.summarySections = selection;
|
|
151
|
+
normalizeSummarySections(config);
|
|
140
152
|
return config as unknown as CompactorConfig;
|
|
141
153
|
}
|
|
142
154
|
|
|
155
|
+
/**
|
|
156
|
+
* The stored section selection: first layer (project wins) with the new
|
|
157
|
+
* array, else the first with a legacy sections object (membership = every id
|
|
158
|
+
* not explicitly false). undefined = nothing stored — the default applies.
|
|
159
|
+
*/
|
|
160
|
+
function storedSummarySelection(cwd: string): string[] | undefined {
|
|
161
|
+
for (const scope of ["project", "global"] as const) {
|
|
162
|
+
let layer: Raw | undefined;
|
|
163
|
+
try {
|
|
164
|
+
layer = getSettingsScoped("compactor", scope, cwd);
|
|
165
|
+
} catch {
|
|
166
|
+
layer = undefined;
|
|
167
|
+
}
|
|
168
|
+
if (!layer) continue;
|
|
169
|
+
if (Array.isArray(layer.summarySections)) return layer.summarySections.map(String);
|
|
170
|
+
if (isRecord(layer.sections)) {
|
|
171
|
+
const legacy = layer.sections;
|
|
172
|
+
return SUMMARY_SECTION_IDS.filter((id) => legacy[id] !== false);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* `summarySections` (string[]) present → the internal `sections` object
|
|
180
|
+
* derives from membership; absent → the default selection stands.
|
|
181
|
+
*/
|
|
182
|
+
function normalizeSummarySections(config: Raw): void {
|
|
183
|
+
if (!Array.isArray(config.summarySections)) return;
|
|
184
|
+
const on = new Set(config.summarySections.map(String));
|
|
185
|
+
const sections: Raw = {};
|
|
186
|
+
for (const id of SUMMARY_SECTION_IDS) sections[id] = on.has(id);
|
|
187
|
+
config.sections = sections;
|
|
188
|
+
}
|
|
189
|
+
|
|
143
190
|
/**
|
|
144
191
|
* Write translated legacy keys into each scope's file once, so the settings
|
|
145
192
|
* hub shows the effective values. Additive only: old keys stay, untouched.
|
|
@@ -148,10 +195,18 @@ export function migrateLegacyConfigFiles(cwd: string = process.cwd()): void {
|
|
|
148
195
|
for (const scope of ["global", "project"] as const) {
|
|
149
196
|
try {
|
|
150
197
|
const raw = getSettingsScoped("compactor", scope, cwd);
|
|
151
|
-
if (!raw
|
|
198
|
+
if (!raw) continue;
|
|
199
|
+
// summarySections: derive from a stored legacy sections object once, so
|
|
200
|
+
// the hub shows the effective selection. Additive — booleans stay.
|
|
201
|
+
if (!Array.isArray(raw.summarySections) && isRecord(raw.sections)) {
|
|
202
|
+
const derived = SUMMARY_SECTION_IDS.filter((id) => (raw.sections as Raw)[id] !== false);
|
|
203
|
+
setSettings("compactor", { summarySections: [...derived] }, scope, cwd);
|
|
204
|
+
raw.summarySections = derived;
|
|
205
|
+
}
|
|
206
|
+
if (!("overrideDefaultCompaction" in raw) && !("autoCompaction" in raw)) continue;
|
|
152
207
|
const translated = translateLegacyConfig(raw);
|
|
153
208
|
const patch: Raw = {};
|
|
154
|
-
for (const key of ["method", "trigger", "thresholdPercent", "
|
|
209
|
+
for (const key of ["method", "trigger", "thresholdPercent", "notify", "summarySections"]) {
|
|
155
210
|
if (!(key in raw) && key in translated) patch[key] = translated[key];
|
|
156
211
|
}
|
|
157
212
|
if (Object.keys(patch).length > 0) setSettings("compactor", patch, scope, cwd);
|
|
@@ -4,6 +4,23 @@
|
|
|
4
4
|
|
|
5
5
|
import type { AutoCompactionConfig, CompactorConfig } from "../types.js";
|
|
6
6
|
|
|
7
|
+
/** Percentage-trigger tuning, fixed since the settings were pruned (UNI-50). */
|
|
8
|
+
export const COOLDOWN_MS = 60_000;
|
|
9
|
+
export const REPEAT_MIN_GROWTH_TOKENS = 4_000;
|
|
10
|
+
|
|
11
|
+
/** Summary-section ids in canonical order (hub multiselect `summarySections`). */
|
|
12
|
+
export const SUMMARY_SECTION_IDS = [
|
|
13
|
+
"activeWork",
|
|
14
|
+
"requests",
|
|
15
|
+
"state",
|
|
16
|
+
"decisions",
|
|
17
|
+
"files",
|
|
18
|
+
"commits",
|
|
19
|
+
"errors",
|
|
20
|
+
"lessons",
|
|
21
|
+
"transcript",
|
|
22
|
+
] as const;
|
|
23
|
+
|
|
7
24
|
export const DEFAULT_COMPACTOR_CONFIG: CompactorConfig = {
|
|
8
25
|
method: "vcc",
|
|
9
26
|
piCompact: "follow",
|
|
@@ -23,10 +40,8 @@ export const DEFAULT_COMPACTOR_CONFIG: CompactorConfig = {
|
|
|
23
40
|
lessons: true,
|
|
24
41
|
transcript: true,
|
|
25
42
|
},
|
|
26
|
-
|
|
27
|
-
repeatMinGrowthTokens: 4_000,
|
|
43
|
+
summarySections: [...SUMMARY_SECTION_IDS],
|
|
28
44
|
llmInstructions: "",
|
|
29
|
-
debug: false,
|
|
30
45
|
};
|
|
31
46
|
|
|
32
47
|
/** The percentage-trigger view of a config (input to decideAutoCompaction). */
|
|
@@ -34,8 +49,8 @@ export function autoCompactionOf(config: CompactorConfig): AutoCompactionConfig
|
|
|
34
49
|
return {
|
|
35
50
|
enabled: config.trigger === "percent",
|
|
36
51
|
thresholdPercent: config.thresholdPercent,
|
|
37
|
-
cooldownMs:
|
|
38
|
-
repeatMinGrowthTokens:
|
|
52
|
+
cooldownMs: COOLDOWN_MS,
|
|
53
|
+
repeatMinGrowthTokens: REPEAT_MIN_GROWTH_TOKENS,
|
|
39
54
|
notify: config.notify,
|
|
40
55
|
};
|
|
41
56
|
}
|
|
@@ -88,14 +88,10 @@ export interface CompactorConfig {
|
|
|
88
88
|
/** Lossless summary budget in tokens; 0 = auto (scales with session size). */
|
|
89
89
|
summaryBudgetTokens: number;
|
|
90
90
|
sections: SummarySections;
|
|
91
|
-
/**
|
|
92
|
-
|
|
93
|
-
/** Percentage trigger: new tokens needed to compact again while still above threshold. */
|
|
94
|
-
repeatMinGrowthTokens: number;
|
|
91
|
+
/** Hub multiselect form of `sections`; present → sections derives from it. */
|
|
92
|
+
summarySections?: string[];
|
|
95
93
|
/** Extra instructions for model-written summaries. */
|
|
96
94
|
llmInstructions: string;
|
|
97
|
-
/** Write compaction diagnostics to /tmp/compactor-debug.json. */
|
|
98
|
-
debug: boolean;
|
|
99
95
|
}
|
|
100
96
|
|
|
101
97
|
/** Percentage auto-compaction trigger settings (decision input). */
|