@pi-unipi/unipi 3.0.0-alpha.25 → 3.0.0-alpha.26
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 +9 -0
- package/package.json +21 -21
- package/packages/ask-user/package.json +2 -2
- package/packages/autocomplete/package.json +1 -1
- package/packages/background-tasks/package.json +2 -2
- package/packages/btw/package.json +2 -2
- package/packages/compactor/package.json +3 -3
- package/packages/core/package.json +1 -1
- package/packages/footer/package.json +3 -3
- package/packages/fusion/package.json +4 -4
- package/packages/fusion/src/index.ts +9 -0
- package/packages/info-screen/README.md +2 -2
- package/packages/info-screen/core-groups.ts +6 -2
- package/packages/info-screen/package.json +2 -2
- package/packages/info-screen/pages/inventory.ts +9 -6
- package/packages/info-screen/pages/modules.ts +3 -3
- package/packages/info-screen/pages/usage.ts +1 -0
- package/packages/info-screen/tui/page-kit.ts +7 -6
- package/packages/input-shortcuts/package.json +2 -2
- package/packages/kanboard/package.json +7 -7
- package/packages/long-horizon/index.ts +7 -0
- package/packages/long-horizon/package.json +2 -2
- package/packages/long-horizon/src/gate.ts +70 -1
- package/packages/mcp/package.json +2 -2
- package/packages/memory/package.json +3 -3
- package/packages/notify/package.json +2 -2
- package/packages/skill-registry/package.json +2 -2
- package/packages/subagents/package.json +3 -3
- package/packages/unipi/bundled.js +84 -30
- package/packages/updater/package.json +2 -2
- package/packages/utility/package.json +2 -2
- package/packages/watchdog/package.json +3 -3
- package/packages/web-api/package.json +2 -2
- package/packages/workflow/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,15 @@ 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.26] — 2026-10-04
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- `fusion`: the "Non-trivial shell work since last handoff" nudge fires at most once per agent run instead of repeating on every 4th non-trivial shell command; a sidekick handoff still resets the streak but no longer re-arms the nudge, and a new prompt arms it again. (UNI-87)
|
|
13
|
+
- `long-horizon`: mode-control tools are now truly OFF in pi's active tool set outside their mode (previously registered/active and only hidden from the provider payload), so pi's tool snippets and other modules no longer see them. Fresh and resumed sessions start with them off; resolving into a mode (command, judge, or active owner) turns that mode's tools on. (UNI-90)
|
|
14
|
+
- `info-screen`: scope order now reads biggest first — footer legend `g global p project s session`, Compactor savings tiles global → project → session, Memory tiles global → project → share.
|
|
15
|
+
- `info-screen`: Skills page tags each source with its real scope (`g` for user/agents/unipi/package skills, `p` for project skills) instead of implying session scope; the Tools page's tool list is tagged `s` (the active set is per session).
|
|
16
|
+
- `info-screen`: Usage page leaves one blank line between the model share bar and the ranked model rows.
|
|
17
|
+
|
|
9
18
|
## [3.0.0-alpha.25] — 2026-10-04
|
|
10
19
|
|
|
11
20
|
### Added
|
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.26",
|
|
4
4
|
"description": "All-in-one extension suite for Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -83,26 +83,26 @@
|
|
|
83
83
|
"typebox": "^1.1.38"
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@pi-unipi/ask-user": "3.0.0-alpha.
|
|
87
|
-
"@pi-unipi/background-tasks": "3.0.0-alpha.
|
|
88
|
-
"@pi-unipi/btw": "3.0.0-alpha.
|
|
89
|
-
"@pi-unipi/command-enchantment": "3.0.0-alpha.
|
|
90
|
-
"@pi-unipi/compactor": "3.0.0-alpha.
|
|
91
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
92
|
-
"@pi-unipi/footer": "3.0.0-alpha.
|
|
93
|
-
"@pi-unipi/fusion": "3.0.0-alpha.
|
|
94
|
-
"@pi-unipi/info-screen": "3.0.0-alpha.
|
|
95
|
-
"@pi-unipi/input-shortcuts": "3.0.0-alpha.
|
|
96
|
-
"@pi-unipi/kanboard": "3.0.0-alpha.
|
|
97
|
-
"@pi-unipi/mcp": "3.0.0-alpha.
|
|
98
|
-
"@pi-unipi/memory": "3.0.0-alpha.
|
|
99
|
-
"@pi-unipi/notify": "3.0.0-alpha.
|
|
100
|
-
"@pi-unipi/skill-registry": "3.0.0-alpha.
|
|
101
|
-
"@pi-unipi/subagents": "3.0.0-alpha.
|
|
102
|
-
"@pi-unipi/updater": "3.0.0-alpha.
|
|
103
|
-
"@pi-unipi/utility": "3.0.0-alpha.
|
|
104
|
-
"@pi-unipi/web-api": "3.0.0-alpha.
|
|
105
|
-
"@pi-unipi/workflow": "3.0.0-alpha.
|
|
86
|
+
"@pi-unipi/ask-user": "3.0.0-alpha.26",
|
|
87
|
+
"@pi-unipi/background-tasks": "3.0.0-alpha.26",
|
|
88
|
+
"@pi-unipi/btw": "3.0.0-alpha.26",
|
|
89
|
+
"@pi-unipi/command-enchantment": "3.0.0-alpha.26",
|
|
90
|
+
"@pi-unipi/compactor": "3.0.0-alpha.26",
|
|
91
|
+
"@pi-unipi/core": "3.0.0-alpha.26",
|
|
92
|
+
"@pi-unipi/footer": "3.0.0-alpha.26",
|
|
93
|
+
"@pi-unipi/fusion": "3.0.0-alpha.26",
|
|
94
|
+
"@pi-unipi/info-screen": "3.0.0-alpha.26",
|
|
95
|
+
"@pi-unipi/input-shortcuts": "3.0.0-alpha.26",
|
|
96
|
+
"@pi-unipi/kanboard": "3.0.0-alpha.26",
|
|
97
|
+
"@pi-unipi/mcp": "3.0.0-alpha.26",
|
|
98
|
+
"@pi-unipi/memory": "3.0.0-alpha.26",
|
|
99
|
+
"@pi-unipi/notify": "3.0.0-alpha.26",
|
|
100
|
+
"@pi-unipi/skill-registry": "3.0.0-alpha.26",
|
|
101
|
+
"@pi-unipi/subagents": "3.0.0-alpha.26",
|
|
102
|
+
"@pi-unipi/updater": "3.0.0-alpha.26",
|
|
103
|
+
"@pi-unipi/utility": "3.0.0-alpha.26",
|
|
104
|
+
"@pi-unipi/web-api": "3.0.0-alpha.26",
|
|
105
|
+
"@pi-unipi/workflow": "3.0.0-alpha.26"
|
|
106
106
|
},
|
|
107
107
|
"devDependencies": {
|
|
108
108
|
"@earendil-works/pi-agent-core": "^0.87.1",
|
|
@@ -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.26",
|
|
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.26"
|
|
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.26",
|
|
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.26",
|
|
14
14
|
"turndown": "^7.2.4"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/btw",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
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.26"
|
|
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.26",
|
|
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.26",
|
|
37
|
+
"@pi-unipi/info-screen": "3.0.0-alpha.26",
|
|
38
38
|
"@earendil-works/pi-agent-core": "^0.87.1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/footer",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "Persistent status bar for Unipi — subscribes to UNIPI_EVENTS and renders key stats from all unipi packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"access": "public"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
36
|
-
"@pi-unipi/background-tasks": "3.0.0-alpha.
|
|
35
|
+
"@pi-unipi/core": "3.0.0-alpha.26",
|
|
36
|
+
"@pi-unipi/background-tasks": "3.0.0-alpha.26"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@earendil-works/pi-coding-agent": "^0.87.1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/fusion",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "Devin-style model picker, fusion presets (lead + sidekick), and Local Fusion runtime for UniPi",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"access": "public"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
36
|
-
"@pi-unipi/subagents": "3.0.0-alpha.
|
|
37
|
-
"@pi-unipi/utility": "3.0.0-alpha.
|
|
35
|
+
"@pi-unipi/core": "3.0.0-alpha.26",
|
|
36
|
+
"@pi-unipi/subagents": "3.0.0-alpha.26",
|
|
37
|
+
"@pi-unipi/utility": "3.0.0-alpha.26"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@earendil-works/pi-ai": "^0.87.1",
|
|
@@ -231,6 +231,11 @@ export default function fusionExtension(pi: ExtensionAPI): void {
|
|
|
231
231
|
let leadToolCalls = 0;
|
|
232
232
|
let editNudgedThisTurn = false;
|
|
233
233
|
let bashStreak = 0;
|
|
234
|
+
// The shell-delegation nudge fires at most ONCE per agent run (per user
|
|
235
|
+
// prompt): pi restarts the handler state on every before_agent_start, and
|
|
236
|
+
// sidekick/read_subagent handoffs keep resetting bashStreak, but neither
|
|
237
|
+
// may re-arm the nudge within the same run.
|
|
238
|
+
let bashNudgedThisRun = false;
|
|
234
239
|
|
|
235
240
|
const wakeLine = createSidekickWakeLine({
|
|
236
241
|
isBusy: () => runtime?.isBusy() === true,
|
|
@@ -378,6 +383,7 @@ export default function fusionExtension(pi: ExtensionAPI): void {
|
|
|
378
383
|
// replacing `systemPrompt`, which nukes the cached prefix). The section is
|
|
379
384
|
// present only while Fusion is active.
|
|
380
385
|
pi.on("before_agent_start", (event, ctx) => {
|
|
386
|
+
bashNudgedThisRun = false;
|
|
381
387
|
syncFusionTools(); // safety net: re-sync after any module re-juggled tools
|
|
382
388
|
if (active?.kind === "fusion") {
|
|
383
389
|
event.systemPromptOptions.sections["fusion-lead-policy"] = leadPolicy(identity(ctx));
|
|
@@ -418,8 +424,10 @@ export default function fusionExtension(pi: ExtensionAPI): void {
|
|
|
418
424
|
if (toolName !== "bash") return;
|
|
419
425
|
const command = typeof event.input.command === "string" ? event.input.command : "";
|
|
420
426
|
if (isTrivialShell(command)) return;
|
|
427
|
+
if (bashNudgedThisRun) return;
|
|
421
428
|
bashStreak += 1;
|
|
422
429
|
if (bashStreak < BASH_NUDGE_EVERY) return;
|
|
430
|
+
bashNudgedThisRun = true;
|
|
423
431
|
const annotation = bashNudge(bashStreak);
|
|
424
432
|
const synopsis = "Non-trivial shell work since last handoff";
|
|
425
433
|
const content = [...event.content, { type: "text" as const, text: annotation }];
|
|
@@ -633,6 +641,7 @@ export default function fusionExtension(pi: ExtensionAPI): void {
|
|
|
633
641
|
stopRuntime();
|
|
634
642
|
editNudgedThisTurn = false;
|
|
635
643
|
bashStreak = 0;
|
|
644
|
+
bashNudgedThisRun = false;
|
|
636
645
|
modelBykey.clear();
|
|
637
646
|
active = loadPreset(ctx.cwd ?? process.cwd()).preset.active;
|
|
638
647
|
mirrorStartup(ctx.cwd ?? process.cwd());
|
|
@@ -12,7 +12,7 @@ See what your session costs, what fills its context and what compaction saved yo
|
|
|
12
12
|
|
|
13
13
|
- **Startup splash.** Unicrab says hello when Pi starts. The splash shows how fast Pi got ready, the session you came back to, today's spend and any update. It does not take your keys. It closes after `bootTimeoutMs`.
|
|
14
14
|
- **Dashboard.** `/unipi:info` opens a set of pages. The first page is about this session.
|
|
15
|
-
- **Scope tags.** A small letter before a number gives its scope: `
|
|
15
|
+
- **Scope tags.** A small letter before a number gives its scope: `g` is all projects on this machine, `p` is this project and `s` is this session.
|
|
16
16
|
- **Fast open.** The dashboard opens at once on cached numbers. Then it updates the page that you look at.
|
|
17
17
|
|
|
18
18
|
## Quick start
|
|
@@ -63,7 +63,7 @@ splash waits for that overlay to close.
|
|
|
63
63
|
| Tools | Each tool, with a colour for its source. Active tools have a full dot. |
|
|
64
64
|
| Skills | Each skill, with a colour for where it lives. |
|
|
65
65
|
| Modules | What each UniPi module adds: tools, commands, settings and keys. Other extensions. |
|
|
66
|
-
| Compactor | Tokens and money that compaction saved, for `
|
|
66
|
+
| Compactor | Tokens and money that compaction saved, for `g`, `p` and `s`. Each compaction in this session. |
|
|
67
67
|
| MCP · Memory · Web · Updates · Keys | The state of each module. MCP servers show `g` or `p` for the config that they come from. |
|
|
68
68
|
|
|
69
69
|
### The context bucket
|
|
@@ -14,6 +14,7 @@ import { collectSession, renderSession, sessionData, type SessionCtxLike } from
|
|
|
14
14
|
import { renderUsage, usageData, type UsageRaw } from "./pages/usage.js";
|
|
15
15
|
import { setCompactorHistorySource } from "./pages/modules.js";
|
|
16
16
|
import { inventoryData, renderInventory, renderModules, type ModulesRaw, type Named } from "./pages/inventory.js";
|
|
17
|
+
import type { Scope } from "./palette.js";
|
|
17
18
|
|
|
18
19
|
// ─── load tracking ─────────────────────────────────────────────────────────
|
|
19
20
|
|
|
@@ -222,7 +223,7 @@ export function registerCoreGroups(): void {
|
|
|
222
223
|
const activeCount = active ? items.filter((i) => i.active).length : undefined;
|
|
223
224
|
return inventoryData(items, activeCount);
|
|
224
225
|
},
|
|
225
|
-
render: (pc) => renderInventory(pc, "tools", "no tools registered"),
|
|
226
|
+
render: (pc) => renderInventory(pc, "tools", "no tools registered", "s"),
|
|
226
227
|
});
|
|
227
228
|
|
|
228
229
|
infoRegistry.registerGroup({
|
|
@@ -245,7 +246,10 @@ export function registerCoreGroups(): void {
|
|
|
245
246
|
} catch {
|
|
246
247
|
items = [];
|
|
247
248
|
}
|
|
248
|
-
|
|
249
|
+
// Skills install per machine except project-local ones — the tags make
|
|
250
|
+
// that explicit (most skills are global, not session-scoped).
|
|
251
|
+
const groupScopes: Record<string, Scope> = { project: "p", user: "g", agents: "g", unipi: "g", package: "g" };
|
|
252
|
+
return inventoryData(items, undefined, groupScopes);
|
|
249
253
|
},
|
|
250
254
|
render: (pc) => renderInventory(pc, "skills", "add skills under ~/.pi/agent/skills or .pi/skills"),
|
|
251
255
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/info-screen",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "Unicrab startup splash and the /unipi:info dashboard — this session, context, usage, compaction savings",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
36
|
+
"@pi-unipi/core": "3.0.0-alpha.26"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@earendil-works/pi-coding-agent": "^0.87.1",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
import { compact, fitTo, grid, rightTo, shareBar, type RGB } from "@pi-unipi/core";
|
|
11
11
|
import { visibleWidth } from "@earendil-works/pi-tui";
|
|
12
12
|
import type { GroupData, PageContext } from "../types.js";
|
|
13
|
-
import {
|
|
13
|
+
import type { Scope } from "../palette.js";
|
|
14
|
+
import { dim, empty, legend, muted, section, tag, tiles } from "../tui/page-kit.js";
|
|
14
15
|
|
|
15
16
|
export interface Named {
|
|
16
17
|
name: string;
|
|
@@ -22,6 +23,8 @@ export interface InventoryRaw {
|
|
|
22
23
|
items: Named[];
|
|
23
24
|
groups: Array<[string, number]>;
|
|
24
25
|
activeCount?: number;
|
|
26
|
+
/** Scope per group label, shown as a one-cell tag in the legend. */
|
|
27
|
+
groupScopes?: Record<string, Scope>;
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
const GROUP_COLORS: RGB[] = [[0, 200, 240], [240, 120, 24], [190, 150, 240], [120, 200, 120], [240, 192, 48], [230, 120, 200], [78, 201, 176], [140, 140, 160]];
|
|
@@ -33,7 +36,7 @@ function colorMap(groups: ReadonlyArray<[string, number]>): Map<string, RGB> {
|
|
|
33
36
|
return m;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
|
-
export function inventoryData(items: Named[], activeCount?: number): GroupData {
|
|
39
|
+
export function inventoryData(items: Named[], activeCount?: number, groupScopes?: Record<string, Scope>): GroupData {
|
|
37
40
|
const counts = new Map<string, number>();
|
|
38
41
|
for (const i of items) counts.set(i.group, (counts.get(i.group) ?? 0) + 1);
|
|
39
42
|
const groups = [...counts.entries()].sort((a, b) => b[1] - a[1]);
|
|
@@ -42,7 +45,7 @@ export function inventoryData(items: Named[], activeCount?: number): GroupData {
|
|
|
42
45
|
const gb = groups.findIndex(([g]) => g === b.group);
|
|
43
46
|
return ga - gb || a.name.localeCompare(b.name);
|
|
44
47
|
});
|
|
45
|
-
const raw: InventoryRaw = { items: sorted, groups, activeCount };
|
|
48
|
+
const raw: InventoryRaw = { items: sorted, groups, activeCount, groupScopes };
|
|
46
49
|
return {
|
|
47
50
|
total: { value: String(items.length) },
|
|
48
51
|
groups: { value: groups.map(([g, n]) => `${n} ${g}`).join(", ") },
|
|
@@ -50,7 +53,7 @@ export function inventoryData(items: Named[], activeCount?: number): GroupData {
|
|
|
50
53
|
};
|
|
51
54
|
}
|
|
52
55
|
|
|
53
|
-
export function renderInventory(pc: PageContext, noun: string, emptyHint: string): string[] {
|
|
56
|
+
export function renderInventory(pc: PageContext, noun: string, emptyHint: string, headerScope?: Scope): string[] {
|
|
54
57
|
const raw = pc.data.raw?.raw as InventoryRaw | undefined;
|
|
55
58
|
if (!raw || raw.items.length === 0) return empty(pc, `No ${noun} loaded`, emptyHint);
|
|
56
59
|
const p = pc.paint;
|
|
@@ -63,10 +66,10 @@ export function renderInventory(pc: PageContext, noun: string, emptyHint: string
|
|
|
63
66
|
out.push(...tiles(pc, headline));
|
|
64
67
|
out.push("");
|
|
65
68
|
out.push(fitTo(shareBar(p, raw.groups.map(([g, n]) => ({ value: n, color: colors.get(g)! })), pc.width, "▆"), pc.width));
|
|
66
|
-
out.push(...legend(pc, raw.groups.map(([g, n]) => ({ label: `${g} ${n}`, color: colors.get(g)
|
|
69
|
+
out.push(...legend(pc, raw.groups.map(([g, n]) => ({ label: `${g} ${n}`, color: colors.get(g)!, scope: raw.groupScopes?.[g] }))));
|
|
67
70
|
out.push("");
|
|
68
71
|
|
|
69
|
-
out.push(section(pc, `all ${noun}`, dim(p, "● active ○ off")));
|
|
72
|
+
out.push(section(pc, `all ${noun}`, dim(p, "● active ○ off"), headerScope));
|
|
70
73
|
const items = raw.items.map((i) => {
|
|
71
74
|
const c = colors.get(i.group) ?? [140, 140, 160];
|
|
72
75
|
const mark = i.active === false ? p.rgb(c, "○") : p.rgb(c, "●");
|
|
@@ -110,9 +110,9 @@ export function renderCompactor(pc: PageContext): string[] {
|
|
|
110
110
|
out.push(section(pc, "saved by compaction", dim(p, "tokens not re-sent · ≈ money not spent")));
|
|
111
111
|
out.push(
|
|
112
112
|
...tiles(pc, [
|
|
113
|
-
{ label: "session", value: moneyShort(sess.dollars), sub: `${compact(sess.avoided)} tok · ${sess.count}×`, scope: "s", stops: [[0, 160, 200], [0, 200, 240], [160, 230, 255]] },
|
|
114
|
-
...(hist.project ? [{ label: "project", value: moneyShort(hist.project.dollars), sub: `${compact(hist.project.avoided)} tok · ${hist.project.count}×`, scope: "p" as const, stops: [[70, 150, 70], [120, 200, 120], [190, 240, 190]] as RGB[] }] : []),
|
|
115
113
|
...(hist.global ? [{ label: "global", value: moneyShort(hist.global.dollars), sub: `${compact(hist.global.avoided)} tok · ${hist.global.count}×`, scope: "g" as const, stops: [CRAB.amber, CRAB.gold, CRAB.cream] }] : []),
|
|
114
|
+
...(hist.project ? [{ label: "project", value: moneyShort(hist.project.dollars), sub: `${compact(hist.project.avoided)} tok · ${hist.project.count}×`, scope: "p" as const, stops: [[70, 150, 70], [120, 200, 120], [190, 240, 190]] as RGB[] }] : []),
|
|
115
|
+
{ label: "session", value: moneyShort(sess.dollars), sub: `${compact(sess.avoided)} tok · ${sess.count}×`, scope: "s", stops: [[0, 160, 200], [0, 200, 240], [160, 230, 255]] },
|
|
116
116
|
]),
|
|
117
117
|
);
|
|
118
118
|
|
|
@@ -287,8 +287,8 @@ export function renderMemory(pc: PageContext): string[] {
|
|
|
287
287
|
const out: string[] = [];
|
|
288
288
|
out.push(
|
|
289
289
|
...tiles(pc, [
|
|
290
|
-
{ label: "project", value: compact(raw.projectCount), sub: raw.project, scope: "p" },
|
|
291
290
|
{ label: "global", value: compact(raw.total), sub: "all projects", scope: "g" },
|
|
291
|
+
{ label: "project", value: compact(raw.projectCount), sub: raw.project, scope: "p" },
|
|
292
292
|
{ label: "share", value: `${raw.total ? Math.round((raw.projectCount / raw.total) * 100) : 0}%`, sub: "project / global" },
|
|
293
293
|
]),
|
|
294
294
|
);
|
|
@@ -150,6 +150,7 @@ export function renderUsage(pc: PageContext): string[] {
|
|
|
150
150
|
out.push(fitTo(`${p.rgb(MODEL_COLORS[0]!, "●")} ${p.bold(m[0])} ${dim(p, "every token this month")} ${p.bold(useCost ? money(m[1]) : compact(m[2]))}`, pc.width));
|
|
151
151
|
} else {
|
|
152
152
|
out.push(fitTo(shareBar(p, raw.models.map((m, i) => ({ value: metric(m), color: MODEL_COLORS[i % MODEL_COLORS.length]! })), pc.width, "▆"), pc.width));
|
|
153
|
+
out.push("");
|
|
153
154
|
// Ranked list: bars scaled to the TOTAL (so they read as shares, not "max = full").
|
|
154
155
|
const nameW = Math.min(26, Math.max(...raw.models.map((m) => m[0].length)) + 1);
|
|
155
156
|
const valW = 14;
|
|
@@ -25,13 +25,13 @@ import {
|
|
|
25
25
|
import type { GroupData, PageContext } from "../types.js";
|
|
26
26
|
import { accentRamp, shade, SCOPE_COLOR, SCOPE_WORD, type Scope } from "../palette.js";
|
|
27
27
|
|
|
28
|
-
/** One-cell scope tag: `
|
|
28
|
+
/** One-cell scope tag: `g` global (amber) · `p` project (green) · `s` session (cyan). */
|
|
29
29
|
export function tag(p: Paint, scope: Scope): string {
|
|
30
30
|
return p.bold(p.rgb(SCOPE_COLOR[scope], scope));
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/** `
|
|
34
|
-
export function scopeLegend(p: Paint, scopes: readonly Scope[] = ["
|
|
33
|
+
/** `g global p project s session` — legend for the footer, biggest scope first. */
|
|
34
|
+
export function scopeLegend(p: Paint, scopes: readonly Scope[] = ["g", "p", "s"]): string {
|
|
35
35
|
return scopes.map((sc) => `${tag(p, sc)} ${p.fg("dim", SCOPE_WORD[sc])}`).join(" ");
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -138,13 +138,14 @@ export function rankBars(
|
|
|
138
138
|
});
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
/** `■ input 42% ■ output 9% …` legend, wrapped onto as many rows as needed.
|
|
142
|
-
|
|
141
|
+
/** `■ input 42% ■ output 9% …` legend, wrapped onto as many rows as needed.
|
|
142
|
+
* An entry may carry a one-cell scope tag: `■ g user 30`. */
|
|
143
|
+
export function legend(pc: PageContext, items: ReadonlyArray<{ label: string; color: RGB; scope?: Scope }>): string[] {
|
|
143
144
|
const p = pc.paint;
|
|
144
145
|
const rows: string[] = [];
|
|
145
146
|
let cur = "";
|
|
146
147
|
for (const i of items) {
|
|
147
|
-
const piece = `${p.rgb(i.color, "■")} ${muted(p, i.label)}`;
|
|
148
|
+
const piece = `${p.rgb(i.color, "■")} ${i.scope ? `${tag(p, i.scope)} ` : ""}${muted(p, i.label)}`;
|
|
148
149
|
if (cur && visibleWidth(cur) + 3 + visibleWidth(piece) > pc.width) {
|
|
149
150
|
rows.push(fitTo(cur, pc.width));
|
|
150
151
|
cur = piece;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/input-shortcuts",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "Keyboard shortcuts for stash/restore, undo/redo, clipboard, and thinking toggle — chord-based overlay system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
36
|
+
"@pi-unipi/core": "3.0.0-alpha.26"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@earendil-works/pi-coding-agent": "^0.87.1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/kanboard",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "Kanboard v3 — deferred-work board: CLI/daemon bridge, task runner and skill",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
34
|
+
"@pi-unipi/core": "3.0.0-alpha.26"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@earendil-works/pi-coding-agent": "^0.87.1",
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"themes": []
|
|
49
49
|
},
|
|
50
50
|
"optionalDependencies": {
|
|
51
|
-
"@pi-unipi/kanboard-linux-x64": "3.0.0-alpha.
|
|
52
|
-
"@pi-unipi/kanboard-linux-arm64": "3.0.0-alpha.
|
|
53
|
-
"@pi-unipi/kanboard-darwin-x64": "3.0.0-alpha.
|
|
54
|
-
"@pi-unipi/kanboard-darwin-arm64": "3.0.0-alpha.
|
|
55
|
-
"@pi-unipi/kanboard-win32-x64": "3.0.0-alpha.
|
|
51
|
+
"@pi-unipi/kanboard-linux-x64": "3.0.0-alpha.26",
|
|
52
|
+
"@pi-unipi/kanboard-linux-arm64": "3.0.0-alpha.26",
|
|
53
|
+
"@pi-unipi/kanboard-darwin-x64": "3.0.0-alpha.26",
|
|
54
|
+
"@pi-unipi/kanboard-darwin-arm64": "3.0.0-alpha.26",
|
|
55
|
+
"@pi-unipi/kanboard-win32-x64": "3.0.0-alpha.26"
|
|
56
56
|
}
|
|
57
57
|
}
|
|
@@ -78,6 +78,11 @@ export default function longHorizon(pi: ExtensionAPI): void {
|
|
|
78
78
|
const owner = new OwnerCoordinator({
|
|
79
79
|
statePath,
|
|
80
80
|
onChange: (_snapshot, event: OwnerEvent) => {
|
|
81
|
+
// Owner transitions sync pi's active set (UNI-90): activate/resume/restore
|
|
82
|
+
// outside before_agent_start must expose the owner's mode tools at once.
|
|
83
|
+
// `gate` below is initialized in this same synchronous body before any
|
|
84
|
+
// transition can fire, so the forward reference is safe.
|
|
85
|
+
gate.onOwnerChanged(event);
|
|
81
86
|
if (ownerEventClearsStash(event)) {
|
|
82
87
|
stash.take();
|
|
83
88
|
stashMetaState = { meta: undefined, kickoff: false };
|
|
@@ -378,6 +383,8 @@ export default function longHorizon(pi: ExtensionAPI): void {
|
|
|
378
383
|
// reattaches the owner's tool surface; the continuation arms a recovery
|
|
379
384
|
// fragment for the first post-restart turn.
|
|
380
385
|
pi.on("session_start", () => {
|
|
386
|
+
// Mode tools start OFF; a restored active owner re-syncs them ON below.
|
|
387
|
+
gate.resetModeTools();
|
|
381
388
|
const restored = owner.restore();
|
|
382
389
|
machine.restore();
|
|
383
390
|
// Only a truly drivable goal arms the recovery fragment; a paused goal
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/long-horizon",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "Long-horizon execution for Pi — mode-gated /goal /ralph /swarm /graph orchestration with a TypeSafe prompt judge",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
40
|
+
"@pi-unipi/core": "3.0.0-alpha.26"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@earendil-works/pi-coding-agent": "^0.87.1",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
20
20
|
import { emitEvent, isChildProcess, UNIPI_EVENTS, setSharedLongHorizonMode } from "@pi-unipi/core";
|
|
21
21
|
import { LH_MODES, MODE_REGISTRY, modeForOwnerKind, type LhMode } from "./modes.js";
|
|
22
|
-
import type { OwnerCoordinator, OwnerState } from "./owner.js";
|
|
22
|
+
import type { OwnerCoordinator, OwnerEvent, OwnerState } from "./owner.js";
|
|
23
23
|
import { resolveMode, type ResolutionSource } from "./judge/resolve.js";
|
|
24
24
|
import type { FetchLike } from "./judge/typesafe.js";
|
|
25
25
|
import { loadSettings, type LongHorizonSettings } from "./settings.js";
|
|
@@ -102,6 +102,28 @@ export function filterPayloadTools<T>(payload: T, mode: LhMode): T {
|
|
|
102
102
|
return { ...record, tools: filtered } as T;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* The gate owns mode-tool membership in pi's ACTIVE tool set: the payload
|
|
107
|
+
* filter alone leaves the tools declared in the system prompt, visible to
|
|
108
|
+
* pi's active-set consumers and to every other module. Touches ONLY names in
|
|
109
|
+
* ALL_MODE_TOOLS — adds the mode's controlTools, removes the rest — and
|
|
110
|
+
* preserves the order of every other tool. Skips setActiveTools when
|
|
111
|
+
* membership already matches (prefix-cache and re-entrancy safety). Returns
|
|
112
|
+
* whether the active set changed. (pi ignores unregistered names; harmless.)
|
|
113
|
+
*/
|
|
114
|
+
export function syncModeTools(
|
|
115
|
+
pi: Pick<ExtensionAPI, "getActiveTools" | "setActiveTools">,
|
|
116
|
+
mode: LhMode,
|
|
117
|
+
): boolean {
|
|
118
|
+
const current = pi.getActiveTools();
|
|
119
|
+
const wanted = new Set<string>(MODE_REGISTRY[mode].controlTools);
|
|
120
|
+
if (ALL_MODE_TOOLS.every((name) => current.includes(name) === wanted.has(name))) return false;
|
|
121
|
+
const kept = current.filter((name) => !ALL_MODE_TOOLS.includes(name) || wanted.has(name));
|
|
122
|
+
const additions = [...wanted].filter((name) => !kept.includes(name));
|
|
123
|
+
pi.setActiveTools([...kept, ...additions]);
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
|
|
105
127
|
function ownerPresenceLine(owner?: OwnerState, parked?: OwnerState): string {
|
|
106
128
|
// Presence only — numbers change every settlement and would bust the prefix
|
|
107
129
|
// cache. Live status rides tail messages (continuation hints), not here.
|
|
@@ -152,6 +174,10 @@ export class Gate {
|
|
|
152
174
|
setExplicit(mode: LhMode): void {
|
|
153
175
|
this.pendingExplicit = mode;
|
|
154
176
|
this.sessionOverride = null;
|
|
177
|
+
// The command's own next turn re-resolves and re-syncs; syncing now covers
|
|
178
|
+
// the gap where the kickoff rides a path that never re-enters
|
|
179
|
+
// before_agent_start (mid-run stash delivery → agent.continue()).
|
|
180
|
+
if (this.pi) syncModeTools(this.pi, mode);
|
|
155
181
|
}
|
|
156
182
|
|
|
157
183
|
/** Pin the session to a mode (/unipi:regular, goal stop): every later turn resolves to it. */
|
|
@@ -160,6 +186,7 @@ export class Gate {
|
|
|
160
186
|
this.pendingExplicit = null;
|
|
161
187
|
setSharedLongHorizonMode(mode);
|
|
162
188
|
if (this.pi) {
|
|
189
|
+
syncModeTools(this.pi, mode);
|
|
163
190
|
emitEvent(this.pi, UNIPI_EVENTS.LONG_HORIZON_MODE_RESOLVED, {
|
|
164
191
|
mode,
|
|
165
192
|
source: "explicit",
|
|
@@ -167,6 +194,43 @@ export class Gate {
|
|
|
167
194
|
}
|
|
168
195
|
}
|
|
169
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Owner transitions between turns sync the active set too (UNI-90): an owner
|
|
199
|
+
* activated, resumed, or restored OUTSIDE before_agent_start — slash
|
|
200
|
+
* commands, command runners (/unipi:continue, unipi:goal-start/resume),
|
|
201
|
+
* crash recovery — flips its mode's tools on immediately, so a kickoff
|
|
202
|
+
* delivered via agent.continue() (which never re-enters
|
|
203
|
+
* before_agent_start) still has them. Wired from index's single owner
|
|
204
|
+
* onChange closure; suspend/finish/clear deliberately sync nothing (the
|
|
205
|
+
* next turn's resolution is authoritative, and a suspend-and-switch already
|
|
206
|
+
* synced its new mode via setExplicit). Best-effort: never throws.
|
|
207
|
+
*/
|
|
208
|
+
onOwnerChanged(event: OwnerEvent): void {
|
|
209
|
+
try {
|
|
210
|
+
if (!this.pi) return;
|
|
211
|
+
if (event.type !== "activated" && event.type !== "resumed" && event.type !== "restored") return;
|
|
212
|
+
if (isChildProcess() && process.env.UNIPI_LH_ALLOW_CHILD !== "1") return;
|
|
213
|
+
const owner = event.type === "restored" ? event.snapshot.active : event.owner;
|
|
214
|
+
if (!owner) return;
|
|
215
|
+
syncModeTools(this.pi, modeForOwnerKind(owner.kind));
|
|
216
|
+
} catch {
|
|
217
|
+
// An owner transition must never fail because of tool syncing.
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Fresh or resumed sessions start with the mode tools OFF; a restored
|
|
222
|
+
* active owner re-syncs its mode's tools ON right after via
|
|
223
|
+
* onOwnerChanged. Wired from index's single session_start handler so the
|
|
224
|
+
* extension keeps exactly one. Best-effort: never throws. */
|
|
225
|
+
resetModeTools(): void {
|
|
226
|
+
try {
|
|
227
|
+
if (!this.pi) return;
|
|
228
|
+
syncModeTools(this.pi, "none");
|
|
229
|
+
} catch {
|
|
230
|
+
// Session start must never fail because of tool syncing.
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
170
234
|
current(): GateState | null {
|
|
171
235
|
return this.turn;
|
|
172
236
|
}
|
|
@@ -221,6 +285,11 @@ export class Gate {
|
|
|
221
285
|
// background by the entry renderer registered in index.ts.
|
|
222
286
|
pi.on("before_agent_start", async (event) => {
|
|
223
287
|
const state = await this.resolveForTurn(event.prompt);
|
|
288
|
+
// pi's live loadout is authoritative for this turn (agent-session honors
|
|
289
|
+
// setActiveTools from before_agent_start unless a handler edited
|
|
290
|
+
// systemPromptOptions.selectedTools), so flip the mode tools before
|
|
291
|
+
// anything else consumes the resolution.
|
|
292
|
+
syncModeTools(pi, state.mode);
|
|
224
293
|
// Shared holder the footer pulls each render (timing-independent), plus
|
|
225
294
|
// the event for the badge/other listeners.
|
|
226
295
|
setSharedLongHorizonMode(state.mode);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/mcp",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "MCP server management extension for Pi coding agent — browse, add, configure, and use MCP servers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"test": "npx tsx --test tests/**/*.test.ts"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
33
|
+
"@pi-unipi/core": "3.0.0-alpha.26"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"@earendil-works/pi-coding-agent": "^0.87.1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/memory",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "Persistent cross-session memory on MemPalace (auto-installed): daemon-routed writes, shared palace with other agents, for Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"README.md"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
46
|
-
"@pi-unipi/info-screen": "3.0.0-alpha.
|
|
45
|
+
"@pi-unipi/core": "3.0.0-alpha.26",
|
|
46
|
+
"@pi-unipi/info-screen": "3.0.0-alpha.26",
|
|
47
47
|
"js-yaml": "^4.1.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/notify",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "Cross-platform notification extension for Pi — native OS, Gotify, and Telegram notifications for agent lifecycle events",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
37
|
+
"@pi-unipi/core": "3.0.0-alpha.26",
|
|
38
38
|
"node-notifier": "^10.0.1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/skill-registry",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "Skill registry for pi — per-project skill on/off, a skill vault, and jev-judged skill exposure",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"main": "index.ts",
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
36
|
+
"@pi-unipi/core": "3.0.0-alpha.26"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@earendil-works/pi-coding-agent": "^0.87.1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/subagents",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "Subagents for UniPi — Devin-model independent child agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"test": "npx tsx --test src/__tests__/*.test.ts"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
13
|
-
"@pi-unipi/utility": "3.0.0-alpha.
|
|
12
|
+
"@pi-unipi/core": "3.0.0-alpha.26",
|
|
13
|
+
"@pi-unipi/utility": "3.0.0-alpha.26",
|
|
14
14
|
"@earendil-works/pi-agent-core": "^0.87.1",
|
|
15
15
|
"acorn": "8.18.0"
|
|
16
16
|
},
|
|
@@ -3432,7 +3432,7 @@ var init_hub = __esm({
|
|
|
3432
3432
|
const rows = [];
|
|
3433
3433
|
for (const def of defs) {
|
|
3434
3434
|
const layers = settingsLayers(def.namespace, this.cwd);
|
|
3435
|
-
const
|
|
3435
|
+
const tag3 = `${layers.global ? "G" : "-"}${layers.project ? "P" : "-"}`;
|
|
3436
3436
|
const sections = def.schema;
|
|
3437
3437
|
const advanced = sections.filter((sec) => sec.advanced === true);
|
|
3438
3438
|
const plain = sections.filter((sec) => sec.advanced !== true);
|
|
@@ -3441,7 +3441,7 @@ var init_hub = __esm({
|
|
|
3441
3441
|
kind: "header",
|
|
3442
3442
|
id: `${def.namespace}::${section3.title}`,
|
|
3443
3443
|
label: `${def.label} \u2014 ${section3.title}`,
|
|
3444
|
-
layerTag:
|
|
3444
|
+
layerTag: tag3,
|
|
3445
3445
|
namespace: def.namespace
|
|
3446
3446
|
});
|
|
3447
3447
|
for (const field of section3.fields) {
|
|
@@ -3468,7 +3468,7 @@ var init_hub = __esm({
|
|
|
3468
3468
|
kind: "header",
|
|
3469
3469
|
id: `${def.namespace}::${section3.title}`,
|
|
3470
3470
|
label: `${def.label} \u2014 ${section3.title}`,
|
|
3471
|
-
layerTag:
|
|
3471
|
+
layerTag: tag3,
|
|
3472
3472
|
namespace: def.namespace,
|
|
3473
3473
|
advancedOf: def.namespace
|
|
3474
3474
|
});
|
|
@@ -4107,8 +4107,8 @@ var init_hub = __esm({
|
|
|
4107
4107
|
return this.exactRow(dim(` ${row.label}`), inner);
|
|
4108
4108
|
}
|
|
4109
4109
|
if (row.kind === "header") {
|
|
4110
|
-
const
|
|
4111
|
-
return hubHeaderBand({ inner, text: `${row.label}${
|
|
4110
|
+
const tag3 = row.layerTag ? ` [${row.layerTag}]` : "";
|
|
4111
|
+
return hubHeaderBand({ inner, text: `${row.label}${tag3}`, namespace: row.namespace });
|
|
4112
4112
|
}
|
|
4113
4113
|
const field = row.field;
|
|
4114
4114
|
if (field.type === "page") {
|
|
@@ -11910,9 +11910,9 @@ function splitCommand(command) {
|
|
|
11910
11910
|
const heredoc = /<<-?\s*(['"]?)([A-Za-z_][A-Za-z0-9_]*)\1[^\n]*\n/g;
|
|
11911
11911
|
let at = 0;
|
|
11912
11912
|
for (let m = heredoc.exec(command); m; m = heredoc.exec(command)) {
|
|
11913
|
-
const
|
|
11913
|
+
const tag3 = m[2];
|
|
11914
11914
|
const bodyStart = m.index + m[0].length;
|
|
11915
|
-
const end = new RegExp(`^[\\t ]*${
|
|
11915
|
+
const end = new RegExp(`^[\\t ]*${tag3}[\\t ]*$`, "m");
|
|
11916
11916
|
const rest = command.slice(bodyStart);
|
|
11917
11917
|
const close = end.exec(rest);
|
|
11918
11918
|
if (!close) continue;
|
|
@@ -16175,6 +16175,15 @@ function filterPayloadTools(payload, mode) {
|
|
|
16175
16175
|
if (filtered.length === tools.length) return payload;
|
|
16176
16176
|
return { ...record, tools: filtered };
|
|
16177
16177
|
}
|
|
16178
|
+
function syncModeTools(pi, mode) {
|
|
16179
|
+
const current = pi.getActiveTools();
|
|
16180
|
+
const wanted = new Set(MODE_REGISTRY[mode].controlTools);
|
|
16181
|
+
if (ALL_MODE_TOOLS.every((name) => current.includes(name) === wanted.has(name))) return false;
|
|
16182
|
+
const kept = current.filter((name) => !ALL_MODE_TOOLS.includes(name) || wanted.has(name));
|
|
16183
|
+
const additions = [...wanted].filter((name) => !kept.includes(name));
|
|
16184
|
+
pi.setActiveTools([...kept, ...additions]);
|
|
16185
|
+
return true;
|
|
16186
|
+
}
|
|
16178
16187
|
function ownerPresenceLine(owner, parked) {
|
|
16179
16188
|
const parts = [];
|
|
16180
16189
|
if (owner) parts.push(`an active ${owner.kind} owner is driving this session`);
|
|
@@ -16216,6 +16225,7 @@ var Gate = class {
|
|
|
16216
16225
|
setExplicit(mode) {
|
|
16217
16226
|
this.pendingExplicit = mode;
|
|
16218
16227
|
this.sessionOverride = null;
|
|
16228
|
+
if (this.pi) syncModeTools(this.pi, mode);
|
|
16219
16229
|
}
|
|
16220
16230
|
/** Pin the session to a mode (/unipi:regular, goal stop): every later turn resolves to it. */
|
|
16221
16231
|
setSessionMode(mode) {
|
|
@@ -16223,12 +16233,46 @@ var Gate = class {
|
|
|
16223
16233
|
this.pendingExplicit = null;
|
|
16224
16234
|
setSharedLongHorizonMode(mode);
|
|
16225
16235
|
if (this.pi) {
|
|
16236
|
+
syncModeTools(this.pi, mode);
|
|
16226
16237
|
emitEvent(this.pi, UNIPI_EVENTS.LONG_HORIZON_MODE_RESOLVED, {
|
|
16227
16238
|
mode,
|
|
16228
16239
|
source: "explicit"
|
|
16229
16240
|
});
|
|
16230
16241
|
}
|
|
16231
16242
|
}
|
|
16243
|
+
/**
|
|
16244
|
+
* Owner transitions between turns sync the active set too (UNI-90): an owner
|
|
16245
|
+
* activated, resumed, or restored OUTSIDE before_agent_start — slash
|
|
16246
|
+
* commands, command runners (/unipi:continue, unipi:goal-start/resume),
|
|
16247
|
+
* crash recovery — flips its mode's tools on immediately, so a kickoff
|
|
16248
|
+
* delivered via agent.continue() (which never re-enters
|
|
16249
|
+
* before_agent_start) still has them. Wired from index's single owner
|
|
16250
|
+
* onChange closure; suspend/finish/clear deliberately sync nothing (the
|
|
16251
|
+
* next turn's resolution is authoritative, and a suspend-and-switch already
|
|
16252
|
+
* synced its new mode via setExplicit). Best-effort: never throws.
|
|
16253
|
+
*/
|
|
16254
|
+
onOwnerChanged(event) {
|
|
16255
|
+
try {
|
|
16256
|
+
if (!this.pi) return;
|
|
16257
|
+
if (event.type !== "activated" && event.type !== "resumed" && event.type !== "restored") return;
|
|
16258
|
+
if (isChildProcess() && process.env.UNIPI_LH_ALLOW_CHILD !== "1") return;
|
|
16259
|
+
const owner = event.type === "restored" ? event.snapshot.active : event.owner;
|
|
16260
|
+
if (!owner) return;
|
|
16261
|
+
syncModeTools(this.pi, modeForOwnerKind(owner.kind));
|
|
16262
|
+
} catch {
|
|
16263
|
+
}
|
|
16264
|
+
}
|
|
16265
|
+
/** Fresh or resumed sessions start with the mode tools OFF; a restored
|
|
16266
|
+
* active owner re-syncs its mode's tools ON right after via
|
|
16267
|
+
* onOwnerChanged. Wired from index's single session_start handler so the
|
|
16268
|
+
* extension keeps exactly one. Best-effort: never throws. */
|
|
16269
|
+
resetModeTools() {
|
|
16270
|
+
try {
|
|
16271
|
+
if (!this.pi) return;
|
|
16272
|
+
syncModeTools(this.pi, "none");
|
|
16273
|
+
} catch {
|
|
16274
|
+
}
|
|
16275
|
+
}
|
|
16232
16276
|
current() {
|
|
16233
16277
|
return this.turn;
|
|
16234
16278
|
}
|
|
@@ -16270,6 +16314,7 @@ var Gate = class {
|
|
|
16270
16314
|
this.pi = pi;
|
|
16271
16315
|
pi.on("before_agent_start", async (event) => {
|
|
16272
16316
|
const state2 = await this.resolveForTurn(event.prompt);
|
|
16317
|
+
syncModeTools(pi, state2.mode);
|
|
16273
16318
|
setSharedLongHorizonMode(state2.mode);
|
|
16274
16319
|
emitEvent(pi, UNIPI_EVENTS.LONG_HORIZON_MODE_RESOLVED, {
|
|
16275
16320
|
mode: state2.mode,
|
|
@@ -18431,6 +18476,7 @@ function longHorizon(pi) {
|
|
|
18431
18476
|
const owner = new OwnerCoordinator({
|
|
18432
18477
|
statePath: statePath2,
|
|
18433
18478
|
onChange: (_snapshot, event) => {
|
|
18479
|
+
gate.onOwnerChanged(event);
|
|
18434
18480
|
if (ownerEventClearsStash(event)) {
|
|
18435
18481
|
stash.take();
|
|
18436
18482
|
stashMetaState = { meta: void 0, kickoff: false };
|
|
@@ -18671,6 +18717,7 @@ function longHorizon(pi) {
|
|
|
18671
18717
|
goalEstimateOn: () => loadSettings().goalProgress !== "off"
|
|
18672
18718
|
}, machine, toolset);
|
|
18673
18719
|
pi.on("session_start", () => {
|
|
18720
|
+
gate.resetModeTools();
|
|
18674
18721
|
const restored = owner.restore();
|
|
18675
18722
|
machine.restore();
|
|
18676
18723
|
const goalStatus = machine.get()?.status;
|
|
@@ -20925,11 +20972,11 @@ function mergeDirs(src, dst, conflictsDir) {
|
|
|
20925
20972
|
} catch {
|
|
20926
20973
|
}
|
|
20927
20974
|
}
|
|
20928
|
-
function moveToConflicts(file,
|
|
20975
|
+
function moveToConflicts(file, tag3) {
|
|
20929
20976
|
if (!file) return;
|
|
20930
20977
|
try {
|
|
20931
20978
|
fs12.mkdirSync(CONFLICTS_DIR(), { recursive: true });
|
|
20932
|
-
const name = `${
|
|
20979
|
+
const name = `${tag3}-${path14.basename(file)}`;
|
|
20933
20980
|
fs12.renameSync(file, path14.join(CONFLICTS_DIR(), name));
|
|
20934
20981
|
} catch {
|
|
20935
20982
|
}
|
|
@@ -22200,7 +22247,7 @@ import { visibleWidth as visibleWidth13 } from "@earendil-works/pi-tui";
|
|
|
22200
22247
|
function tag(p, scope) {
|
|
22201
22248
|
return p.bold(p.rgb(SCOPE_COLOR[scope], scope));
|
|
22202
22249
|
}
|
|
22203
|
-
function scopeLegend(p, scopes = ["
|
|
22250
|
+
function scopeLegend(p, scopes = ["g", "p", "s"]) {
|
|
22204
22251
|
return scopes.map((sc) => `${tag(p, sc)} ${p.fg("dim", SCOPE_WORD[sc])}`).join(" ");
|
|
22205
22252
|
}
|
|
22206
22253
|
function spaced(text) {
|
|
@@ -22269,7 +22316,7 @@ function legend(pc, items) {
|
|
|
22269
22316
|
const rows = [];
|
|
22270
22317
|
let cur = "";
|
|
22271
22318
|
for (const i of items) {
|
|
22272
|
-
const piece = `${p.rgb(i.color, "\u25A0")} ${muted(p, i.label)}`;
|
|
22319
|
+
const piece = `${p.rgb(i.color, "\u25A0")} ${i.scope ? `${tag(p, i.scope)} ` : ""}${muted(p, i.label)}`;
|
|
22273
22320
|
if (cur && visibleWidth13(cur) + 3 + visibleWidth13(piece) > pc.width) {
|
|
22274
22321
|
rows.push(fitTo(cur, pc.width));
|
|
22275
22322
|
cur = piece;
|
|
@@ -22391,9 +22438,9 @@ function renderCompactor(pc) {
|
|
|
22391
22438
|
out.push(section(pc, "saved by compaction", dim2(p, "tokens not re-sent \xB7 \u2248 money not spent")));
|
|
22392
22439
|
out.push(
|
|
22393
22440
|
...tiles(pc, [
|
|
22394
|
-
{ label: "
|
|
22441
|
+
...hist.global ? [{ label: "global", value: moneyShort(hist.global.dollars), sub: `${compact(hist.global.avoided)} tok \xB7 ${hist.global.count}\xD7`, scope: "g", stops: [CRAB.amber, CRAB.gold, CRAB.cream] }] : [],
|
|
22395
22442
|
...hist.project ? [{ label: "project", value: moneyShort(hist.project.dollars), sub: `${compact(hist.project.avoided)} tok \xB7 ${hist.project.count}\xD7`, scope: "p", stops: [[70, 150, 70], [120, 200, 120], [190, 240, 190]] }] : [],
|
|
22396
|
-
|
|
22443
|
+
{ label: "session", value: moneyShort(sess.dollars), sub: `${compact(sess.avoided)} tok \xB7 ${sess.count}\xD7`, scope: "s", stops: [[0, 160, 200], [0, 200, 240], [160, 230, 255]] }
|
|
22397
22444
|
])
|
|
22398
22445
|
);
|
|
22399
22446
|
if (best.avoided > 0) {
|
|
@@ -22522,8 +22569,8 @@ function renderMemory(pc) {
|
|
|
22522
22569
|
const out = [];
|
|
22523
22570
|
out.push(
|
|
22524
22571
|
...tiles(pc, [
|
|
22525
|
-
{ label: "project", value: compact(raw.projectCount), sub: raw.project, scope: "p" },
|
|
22526
22572
|
{ label: "global", value: compact(raw.total), sub: "all projects", scope: "g" },
|
|
22573
|
+
{ label: "project", value: compact(raw.projectCount), sub: raw.project, scope: "p" },
|
|
22527
22574
|
{ label: "share", value: `${raw.total ? Math.round(raw.projectCount / raw.total * 100) : 0}%`, sub: "project / global" }
|
|
22528
22575
|
])
|
|
22529
22576
|
);
|
|
@@ -23695,6 +23742,7 @@ function renderUsage(pc) {
|
|
|
23695
23742
|
out.push(fitTo(`${p.rgb(MODEL_COLORS[0], "\u25CF")} ${p.bold(m[0])} ${dim2(p, "every token this month")} ${p.bold(useCost ? money(m[1]) : compact(m[2]))}`, pc.width));
|
|
23696
23743
|
} else {
|
|
23697
23744
|
out.push(fitTo(shareBar(p, raw.models.map((m, i) => ({ value: metric(m), color: MODEL_COLORS[i % MODEL_COLORS.length] })), pc.width, "\u2586"), pc.width));
|
|
23745
|
+
out.push("");
|
|
23698
23746
|
const nameW = Math.min(26, Math.max(...raw.models.map((m) => m[0].length)) + 1);
|
|
23699
23747
|
const valW = 14;
|
|
23700
23748
|
const barW = Math.max(6, pc.width - nameW - valW - 5);
|
|
@@ -23721,7 +23769,7 @@ function colorMap(groups) {
|
|
|
23721
23769
|
groups.forEach(([g], i) => m.set(g, GROUP_COLORS[i % GROUP_COLORS.length]));
|
|
23722
23770
|
return m;
|
|
23723
23771
|
}
|
|
23724
|
-
function inventoryData(items, activeCount) {
|
|
23772
|
+
function inventoryData(items, activeCount, groupScopes) {
|
|
23725
23773
|
const counts = /* @__PURE__ */ new Map();
|
|
23726
23774
|
for (const i of items) counts.set(i.group, (counts.get(i.group) ?? 0) + 1);
|
|
23727
23775
|
const groups = [...counts.entries()].sort((a, b) => b[1] - a[1]);
|
|
@@ -23730,14 +23778,14 @@ function inventoryData(items, activeCount) {
|
|
|
23730
23778
|
const gb = groups.findIndex(([g]) => g === b.group);
|
|
23731
23779
|
return ga - gb || a.name.localeCompare(b.name);
|
|
23732
23780
|
});
|
|
23733
|
-
const raw = { items: sorted, groups, activeCount };
|
|
23781
|
+
const raw = { items: sorted, groups, activeCount, groupScopes };
|
|
23734
23782
|
return {
|
|
23735
23783
|
total: { value: String(items.length) },
|
|
23736
23784
|
groups: { value: groups.map(([g, n]) => `${n} ${g}`).join(", ") },
|
|
23737
23785
|
raw: { value: "", raw }
|
|
23738
23786
|
};
|
|
23739
23787
|
}
|
|
23740
|
-
function renderInventory(pc, noun, emptyHint) {
|
|
23788
|
+
function renderInventory(pc, noun, emptyHint, headerScope) {
|
|
23741
23789
|
const raw = pc.data.raw?.raw;
|
|
23742
23790
|
if (!raw || raw.items.length === 0) return empty(pc, `No ${noun} loaded`, emptyHint);
|
|
23743
23791
|
const p = pc.paint;
|
|
@@ -23749,9 +23797,9 @@ function renderInventory(pc, noun, emptyHint) {
|
|
|
23749
23797
|
out.push(...tiles(pc, headline));
|
|
23750
23798
|
out.push("");
|
|
23751
23799
|
out.push(fitTo(shareBar(p, raw.groups.map(([g2, n]) => ({ value: n, color: colors.get(g2) })), pc.width, "\u2586"), pc.width));
|
|
23752
|
-
out.push(...legend(pc, raw.groups.map(([g2, n]) => ({ label: `${g2} ${n}`, color: colors.get(g2) }))));
|
|
23800
|
+
out.push(...legend(pc, raw.groups.map(([g2, n]) => ({ label: `${g2} ${n}`, color: colors.get(g2), scope: raw.groupScopes?.[g2] }))));
|
|
23753
23801
|
out.push("");
|
|
23754
|
-
out.push(section(pc, `all ${noun}`, dim2(p, "\u25CF active \u25CB off")));
|
|
23802
|
+
out.push(section(pc, `all ${noun}`, dim2(p, "\u25CF active \u25CB off"), headerScope));
|
|
23755
23803
|
const items = raw.items.map((i) => {
|
|
23756
23804
|
const c2 = colors.get(i.group) ?? [140, 140, 160];
|
|
23757
23805
|
const mark = i.active === false ? p.rgb(c2, "\u25CB") : p.rgb(c2, "\u25CF");
|
|
@@ -23983,7 +24031,7 @@ function registerCoreGroups() {
|
|
|
23983
24031
|
const activeCount = active ? items.filter((i) => i.active).length : void 0;
|
|
23984
24032
|
return inventoryData(items, activeCount);
|
|
23985
24033
|
},
|
|
23986
|
-
render: (pc) => renderInventory(pc, "tools", "no tools registered")
|
|
24034
|
+
render: (pc) => renderInventory(pc, "tools", "no tools registered", "s")
|
|
23987
24035
|
});
|
|
23988
24036
|
infoRegistry.registerGroup({
|
|
23989
24037
|
id: "skills",
|
|
@@ -24003,7 +24051,8 @@ function registerCoreGroups() {
|
|
|
24003
24051
|
} catch {
|
|
24004
24052
|
items = [];
|
|
24005
24053
|
}
|
|
24006
|
-
|
|
24054
|
+
const groupScopes = { project: "p", user: "g", agents: "g", unipi: "g", package: "g" };
|
|
24055
|
+
return inventoryData(items, void 0, groupScopes);
|
|
24007
24056
|
},
|
|
24008
24057
|
render: (pc) => renderInventory(pc, "skills", "add skills under ~/.pi/agent/skills or .pi/skills")
|
|
24009
24058
|
});
|
|
@@ -28424,8 +28473,8 @@ function normalizeContextUsage(value) {
|
|
|
28424
28473
|
return { tokens, contextWindow, percent };
|
|
28425
28474
|
}
|
|
28426
28475
|
function parseContextUsageXml(xml) {
|
|
28427
|
-
const readNumber = (
|
|
28428
|
-
const match = new RegExp(`<${
|
|
28476
|
+
const readNumber = (tag3) => {
|
|
28477
|
+
const match = new RegExp(`<${tag3}>(.*?)</${tag3}>`, "i").exec(xml);
|
|
28429
28478
|
if (!match) return void 0;
|
|
28430
28479
|
const raw = match[1]?.trim();
|
|
28431
28480
|
if (raw === "null" || raw === "?") return null;
|
|
@@ -31194,8 +31243,8 @@ function elementToMarkdown(element) {
|
|
|
31194
31243
|
}
|
|
31195
31244
|
} else if (node.nodeType === 1) {
|
|
31196
31245
|
const el = node;
|
|
31197
|
-
const
|
|
31198
|
-
switch (
|
|
31246
|
+
const tag3 = el.tagName?.toLowerCase();
|
|
31247
|
+
switch (tag3) {
|
|
31199
31248
|
case "h1":
|
|
31200
31249
|
lines3.push("");
|
|
31201
31250
|
lines3.push(`# ${extractTextContent(el)}`);
|
|
@@ -31215,7 +31264,7 @@ function elementToMarkdown(element) {
|
|
|
31215
31264
|
case "h5":
|
|
31216
31265
|
case "h6":
|
|
31217
31266
|
lines3.push("");
|
|
31218
|
-
lines3.push(`${"#".repeat(parseInt(
|
|
31267
|
+
lines3.push(`${"#".repeat(parseInt(tag3[1]))} ${extractTextContent(el)}`);
|
|
31219
31268
|
lines3.push("");
|
|
31220
31269
|
break;
|
|
31221
31270
|
case "p":
|
|
@@ -42098,11 +42147,11 @@ function getEnhancedUnipiItems(prefix, descriptionOverrides = /* @__PURE__ */ ne
|
|
|
42098
42147
|
const color2 = PACKAGE_COLORS[pkg] ?? "";
|
|
42099
42148
|
const label = PACKAGE_LABELS[pkg] ?? pkg;
|
|
42100
42149
|
const desc = descriptionOverrides.get(cmd) ?? COMMAND_DESCRIPTIONS[cmd] ?? "";
|
|
42101
|
-
const
|
|
42150
|
+
const tag3 = colorize(color2, `[${label}]`);
|
|
42102
42151
|
return {
|
|
42103
42152
|
value: cmd,
|
|
42104
42153
|
label: cmd,
|
|
42105
|
-
description: desc ? `${
|
|
42154
|
+
description: desc ? `${tag3} ${desc}` : tag3
|
|
42106
42155
|
};
|
|
42107
42156
|
});
|
|
42108
42157
|
}
|
|
@@ -51936,8 +51985,8 @@ var PresetEditor = class {
|
|
|
51936
51985
|
if (m.key === active.lead) tags.push("active lead");
|
|
51937
51986
|
if (m.key === active.sidekick) tags.push("active sidekick");
|
|
51938
51987
|
}
|
|
51939
|
-
const
|
|
51940
|
-
lines3.push(truncateToWidth39(` ${ptr} ${l} ${s} ${name}${
|
|
51988
|
+
const tag3 = tags.length > 0 ? ` ${t.fg("dim", `(${tags.join(", ")})`)}` : "";
|
|
51989
|
+
lines3.push(truncateToWidth39(` ${ptr} ${l} ${s} ${name}${tag3}`, Math.max(1, width - 1)));
|
|
51941
51990
|
}
|
|
51942
51991
|
if (list.length > win) lines3.push(t.fg("dim", ` ${String(start + 1)}-${String(end)} of ${String(list.length)}`));
|
|
51943
51992
|
lines3.push("");
|
|
@@ -52409,6 +52458,7 @@ function fusionExtension(pi) {
|
|
|
52409
52458
|
let leadToolCalls = 0;
|
|
52410
52459
|
let editNudgedThisTurn = false;
|
|
52411
52460
|
let bashStreak = 0;
|
|
52461
|
+
let bashNudgedThisRun = false;
|
|
52412
52462
|
const wakeLine = createSidekickWakeLine({
|
|
52413
52463
|
isBusy: () => runtime?.isBusy() === true,
|
|
52414
52464
|
progress: () => runtime?.progress(),
|
|
@@ -52531,6 +52581,7 @@ Handoffs: ${String(runtime.reports.size)} \xB7 runtime alive: ${String(runtime.i
|
|
|
52531
52581
|
handler: async (_args, ctx) => ctx.ui.notify(savingsStats(ctx), "info")
|
|
52532
52582
|
});
|
|
52533
52583
|
pi.on("before_agent_start", (event, ctx) => {
|
|
52584
|
+
bashNudgedThisRun = false;
|
|
52534
52585
|
syncFusionTools();
|
|
52535
52586
|
if (active?.kind === "fusion") {
|
|
52536
52587
|
event.systemPromptOptions.sections["fusion-lead-policy"] = leadPolicy(identity(ctx));
|
|
@@ -52569,8 +52620,10 @@ Handoffs: ${String(runtime.reports.size)} \xB7 runtime alive: ${String(runtime.i
|
|
|
52569
52620
|
if (toolName !== "bash") return;
|
|
52570
52621
|
const command = typeof event.input.command === "string" ? event.input.command : "";
|
|
52571
52622
|
if (isTrivialShell(command)) return;
|
|
52623
|
+
if (bashNudgedThisRun) return;
|
|
52572
52624
|
bashStreak += 1;
|
|
52573
52625
|
if (bashStreak < BASH_NUDGE_EVERY) return;
|
|
52626
|
+
bashNudgedThisRun = true;
|
|
52574
52627
|
const annotation = bashNudge(bashStreak);
|
|
52575
52628
|
const synopsis = "Non-trivial shell work since last handoff";
|
|
52576
52629
|
const content = [...event.content, { type: "text", text: annotation }];
|
|
@@ -52753,6 +52806,7 @@ ${String(result2.curation.lead.length)} lead \xB7 ${String(result2.curation.side
|
|
|
52753
52806
|
stopRuntime();
|
|
52754
52807
|
editNudgedThisTurn = false;
|
|
52755
52808
|
bashStreak = 0;
|
|
52809
|
+
bashNudgedThisRun = false;
|
|
52756
52810
|
modelBykey.clear();
|
|
52757
52811
|
active = loadPreset(ctx.cwd ?? process.cwd()).preset.active;
|
|
52758
52812
|
mirrorStartup(ctx.cwd ?? process.cwd());
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/updater",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "Auto-updater, changelog browser, and readme browser for Unipi — checks npm registry, renders CHANGELOG.md and README.md files in TUI overlays",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
37
|
+
"@pi-unipi/core": "3.0.0-alpha.26"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@earendil-works/pi-coding-agent": "^0.87.1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/utility",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "Utility commands and tools for Pi coding agent — lifecycle, diagnostics, cache, analytics, display, batch execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"access": "public"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
39
|
+
"@pi-unipi/core": "3.0.0-alpha.26"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {},
|
|
42
42
|
"peerDependencies": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/watchdog",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "Jev watchdog for long-running tool calls and background tasks — judges stuck work via TypeSafe System One and kills or warns",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"access": "public"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@pi-unipi/background-tasks": "3.0.0-alpha.
|
|
36
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
35
|
+
"@pi-unipi/background-tasks": "3.0.0-alpha.26",
|
|
36
|
+
"@pi-unipi/core": "3.0.0-alpha.26"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@earendil-works/pi-coding-agent": "^0.87.1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/web-api",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "Web search, read, and summarize tools with provider-based backend selection for Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"README.md"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
34
|
+
"@pi-unipi/core": "3.0.0-alpha.26",
|
|
35
35
|
"defuddle": "^0.18.1",
|
|
36
36
|
"linkedom": "^0.18.12",
|
|
37
37
|
"wreq-js": "^2.3.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/workflow",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.26",
|
|
4
4
|
"description": "Plan mode and permission modes for the Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
30
|
+
"@pi-unipi/core": "3.0.0-alpha.26"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@earendil-works/pi-ai": "^0.87.1",
|