pi-ui-extend 1.0.37 → 1.0.39
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/README.md +1 -0
- package/dist/app/extensions/extension-ui-controller.js +3 -0
- package/dist/app/rendering/extension-entry-renderer.js +2 -0
- package/dist/bundled-extensions/question/contract.d.ts +3 -0
- package/dist/bundled-extensions/question/contract.js +24 -0
- package/dist/bundled-extensions/question/desktop.d.ts +11 -0
- package/dist/bundled-extensions/question/desktop.js +143 -0
- package/dist/bundled-extensions/question/index.d.ts +1 -0
- package/dist/bundled-extensions/question/index.js +5 -1
- package/dist/bundled-extensions/question/render.js +6 -0
- package/dist/bundled-extensions/question/result.js +71 -9
- package/dist/bundled-extensions/question/tool-description.js +4 -3
- package/dist/bundled-extensions/question/tui.js +127 -12
- package/dist/bundled-extensions/question/types.d.ts +23 -2
- package/dist/tool-renderers/question.js +20 -1
- package/docs/desktop-markdown-media.md +77 -0
- package/docs/desktop-mvp.md +134 -0
- package/docs/desktop-task-manager.md +124 -0
- package/external/pi-tools-suite/package.json +3 -3
- package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +9 -1
- package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +41 -2
- package/external/pi-tools-suite/src/async-subagents/core/config.ts +13 -1
- package/external/pi-tools-suite/src/async-subagents/index.ts +6 -2
- package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/SKILL.md +65 -22
- package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/qa-design.md +23 -8
- package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/scripts/browser-qa-runner.mjs +180 -41
- package/external/pi-tools-suite/src/async-subagents/subagent-overlay.ts +1 -1
- package/external/pi-tools-suite/src/dcp/auto-compress.ts +1 -1
- package/external/pi-tools-suite/src/dcp/compression-blocks.ts +1 -51
- package/external/pi-tools-suite/src/dcp/debug-log.ts +6 -0
- package/external/pi-tools-suite/src/dcp/index.ts +27 -125
- package/external/pi-tools-suite/src/dcp/prompts.ts +2 -2
- package/external/pi-tools-suite/src/dcp/provider-tool-results.ts +2 -1
- package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +31 -10
- package/external/pi-tools-suite/src/dcp/pruner-compression-blocks.ts +6 -7
- package/external/pi-tools-suite/src/dcp/pruner-message-ids.ts +153 -111
- package/external/pi-tools-suite/src/dcp/pruner-metadata.ts +28 -17
- package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +61 -27
- package/external/pi-tools-suite/src/dcp/pruner.ts +21 -13
- package/external/pi-tools-suite/src/dcp/state.ts +59 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +4 -0
- package/external/pi-tools-suite/src/lib/rpc-session-state.ts +34 -0
- package/external/pi-tools-suite/src/todo/todo.ts +8 -3
- package/package.json +10 -4
|
@@ -119,6 +119,8 @@ export interface DcpNudgeAnchor {
|
|
|
119
119
|
turnIndex: number
|
|
120
120
|
/** Approximate context usage that triggered the reminder, as a 0-1 fraction. */
|
|
121
121
|
contextPercent?: number
|
|
122
|
+
/** Frozen provider-visible reminder text for cache-stable reapplication. */
|
|
123
|
+
renderedReminder?: string
|
|
122
124
|
/** Wall-clock creation time. */
|
|
123
125
|
createdAt: number
|
|
124
126
|
/** Wall-clock time of the latest re-application/update. */
|
|
@@ -166,6 +168,10 @@ export interface DcpState {
|
|
|
166
168
|
messageIdSnapshot: Map<string, number>
|
|
167
169
|
/** Extra metadata for the model-visible DCP message IDs in messageIdSnapshot. */
|
|
168
170
|
messageMetaSnapshot: Map<string, MessageIdMeta>
|
|
171
|
+
/** Stable message identity → persistent model-visible mNNN assignment. */
|
|
172
|
+
messageIdsByStableId: Map<string, string>
|
|
173
|
+
/** Monotonic counter for persistent message IDs. */
|
|
174
|
+
nextMessageId: number
|
|
169
175
|
|
|
170
176
|
// ── Turn tracking ──────────────────────────────────────────────────────────
|
|
171
177
|
/**
|
|
@@ -174,6 +180,10 @@ export interface DcpState {
|
|
|
174
180
|
* context array in the `context` event handler.
|
|
175
181
|
*/
|
|
176
182
|
currentTurn: number
|
|
183
|
+
/** Last user-turn count at which automatic prune discovery ran. */
|
|
184
|
+
lastAutomaticPruneTurn: number
|
|
185
|
+
/** Highest created block ID at the last automatic-prune checkpoint. */
|
|
186
|
+
lastAutomaticPruneBlockId: number
|
|
177
187
|
|
|
178
188
|
// ── Statistics ─────────────────────────────────────────────────────────────
|
|
179
189
|
/** Running total of tokens estimated to have been saved by pruning/compression */
|
|
@@ -253,7 +263,11 @@ export function createState(): DcpState {
|
|
|
253
263
|
nextBlockId: 1,
|
|
254
264
|
messageIdSnapshot: new Map(),
|
|
255
265
|
messageMetaSnapshot: new Map(),
|
|
266
|
+
messageIdsByStableId: new Map(),
|
|
267
|
+
nextMessageId: 1,
|
|
256
268
|
currentTurn: 0,
|
|
269
|
+
lastAutomaticPruneTurn: -1,
|
|
270
|
+
lastAutomaticPruneBlockId: 0,
|
|
257
271
|
tokensSaved: 0,
|
|
258
272
|
totalPruneCount: 0,
|
|
259
273
|
totalToolCallCount: 0,
|
|
@@ -285,7 +299,11 @@ export function resetState(state: DcpState): void {
|
|
|
285
299
|
state.nextBlockId = 1
|
|
286
300
|
state.messageIdSnapshot.clear()
|
|
287
301
|
state.messageMetaSnapshot.clear()
|
|
302
|
+
state.messageIdsByStableId.clear()
|
|
303
|
+
state.nextMessageId = 1
|
|
288
304
|
state.currentTurn = 0
|
|
305
|
+
state.lastAutomaticPruneTurn = -1
|
|
306
|
+
state.lastAutomaticPruneBlockId = 0
|
|
289
307
|
state.tokensSaved = 0
|
|
290
308
|
state.totalPruneCount = 0
|
|
291
309
|
state.totalToolCallCount = 0
|
|
@@ -424,12 +442,18 @@ export interface SerializedDcpState {
|
|
|
424
442
|
nudgeAnchors?: DcpNudgeAnchor[]
|
|
425
443
|
nextNudgeAnchorId?: number
|
|
426
444
|
lastNudge?: DcpLastNudge
|
|
445
|
+
/** Persistent stable-identity → mNNN assignments. */
|
|
446
|
+
messageIdsByStableId?: Array<[string, string]>
|
|
447
|
+
nextMessageId?: number
|
|
427
448
|
/**
|
|
428
449
|
* Persisted since v??. `context` events re-seed currentTurn from raw
|
|
429
450
|
* messages, but keeping it across session restarts gives diagnostics and
|
|
430
451
|
* telemetry a contiguous turn counter instead of resetting to 0.
|
|
431
452
|
*/
|
|
432
453
|
currentTurn?: number
|
|
454
|
+
/** Automatic pruning runs only when one of these checkpoints changes. */
|
|
455
|
+
lastAutomaticPruneTurn?: number
|
|
456
|
+
lastAutomaticPruneBlockId?: number
|
|
433
457
|
/**
|
|
434
458
|
* Persisted since v?.?. Without persistence a pi process restart silently
|
|
435
459
|
* reset the nudge cadence counter to 0, which could suppress the next
|
|
@@ -622,7 +646,11 @@ export function serializeState(state: DcpState): SerializedDcpState {
|
|
|
622
646
|
nudgeAnchors: state.nudgeAnchors,
|
|
623
647
|
nextNudgeAnchorId: state.nextNudgeAnchorId,
|
|
624
648
|
lastNudge: state.lastNudge,
|
|
649
|
+
messageIdsByStableId: Array.from(state.messageIdsByStableId.entries()),
|
|
650
|
+
nextMessageId: state.nextMessageId,
|
|
625
651
|
currentTurn: state.currentTurn,
|
|
652
|
+
lastAutomaticPruneTurn: state.lastAutomaticPruneTurn,
|
|
653
|
+
lastAutomaticPruneBlockId: state.lastAutomaticPruneBlockId,
|
|
626
654
|
nudgeCounter: state.nudgeCounter,
|
|
627
655
|
lastNudgeTurn: state.lastNudgeTurn,
|
|
628
656
|
lastContextWindow: state.lastContextWindow,
|
|
@@ -776,6 +804,31 @@ export function restoreState(state: DcpState, data: unknown): void {
|
|
|
776
804
|
state.lastNudge = saved.lastNudge
|
|
777
805
|
}
|
|
778
806
|
|
|
807
|
+
if (Array.isArray(saved.messageIdsByStableId)) {
|
|
808
|
+
const usedIds = new Set<string>()
|
|
809
|
+
const usedStableKeys = new Set<string>()
|
|
810
|
+
state.messageIdsByStableId = new Map(
|
|
811
|
+
saved.messageIdsByStableId.filter(
|
|
812
|
+
(entry): entry is [string, string] => {
|
|
813
|
+
if (!Array.isArray(entry) || typeof entry[0] !== "string" || typeof entry[1] !== "string") return false
|
|
814
|
+
if (!entry[0] || !/^m\d+$/.test(entry[1]) || usedIds.has(entry[1]) || usedStableKeys.has(entry[0])) return false
|
|
815
|
+
usedIds.add(entry[1])
|
|
816
|
+
usedStableKeys.add(entry[0])
|
|
817
|
+
return true
|
|
818
|
+
},
|
|
819
|
+
),
|
|
820
|
+
)
|
|
821
|
+
}
|
|
822
|
+
const highestRestoredMessageId = Math.max(
|
|
823
|
+
0,
|
|
824
|
+
...Array.from(state.messageIdsByStableId.values()).map((id) => Number.parseInt(id.slice(1), 10) || 0),
|
|
825
|
+
)
|
|
826
|
+
if (typeof saved.nextMessageId === "number" && Number.isFinite(saved.nextMessageId) && saved.nextMessageId > 0) {
|
|
827
|
+
state.nextMessageId = Math.max(Math.floor(saved.nextMessageId), highestRestoredMessageId + 1)
|
|
828
|
+
} else {
|
|
829
|
+
state.nextMessageId = highestRestoredMessageId + 1
|
|
830
|
+
}
|
|
831
|
+
|
|
779
832
|
// nudgeCounter: clamp to a non-negative integer so a corrupted payload
|
|
780
833
|
// cannot stall reminders by going negative. Default 0 keeps older sessions
|
|
781
834
|
// behaving like a fresh cadence on first post-restart context event.
|
|
@@ -793,6 +846,12 @@ export function restoreState(state: DcpState, data: unknown): void {
|
|
|
793
846
|
if (typeof saved.currentTurn === "number" && Number.isFinite(saved.currentTurn) && saved.currentTurn >= 0) {
|
|
794
847
|
state.currentTurn = Math.floor(saved.currentTurn)
|
|
795
848
|
}
|
|
849
|
+
if (typeof saved.lastAutomaticPruneTurn === "number" && Number.isFinite(saved.lastAutomaticPruneTurn)) {
|
|
850
|
+
state.lastAutomaticPruneTurn = Math.floor(saved.lastAutomaticPruneTurn)
|
|
851
|
+
}
|
|
852
|
+
if (typeof saved.lastAutomaticPruneBlockId === "number" && Number.isFinite(saved.lastAutomaticPruneBlockId) && saved.lastAutomaticPruneBlockId >= 0) {
|
|
853
|
+
state.lastAutomaticPruneBlockId = Math.floor(saved.lastAutomaticPruneBlockId)
|
|
854
|
+
}
|
|
796
855
|
if (typeof saved.lastContextWindow === "number" && Number.isFinite(saved.lastContextWindow) && saved.lastContextWindow > 0) {
|
|
797
856
|
state.lastContextWindow = saved.lastContextWindow
|
|
798
857
|
}
|
|
@@ -329,6 +329,10 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = String.raw`{
|
|
|
329
329
|
"description": "Use when the sub-agent should make or plan code changes for a feature, bug fix, or refactor.",
|
|
330
330
|
"model": "openai-codex/gpt-5.6-sol",
|
|
331
331
|
"fallbackModels": ["zai/glm-5.3"],
|
|
332
|
+
"modelByParent": {
|
|
333
|
+
"openai-codex/gpt-5.6-luna*": { "model": "openai-codex/gpt-5.6-terra", "fallbackModels": ["zai/glm-5.3"] },
|
|
334
|
+
"openai-codex/gpt-5.6-sol*": { "model": "openai-codex/gpt-5.6-terra", "fallbackModels": ["zai/glm-5.3"] }
|
|
335
|
+
},
|
|
332
336
|
"thinking": "high"
|
|
333
337
|
},
|
|
334
338
|
"deep": {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Reserved RPC widget key consumed by Pix ACP as a structured event envelope. */
|
|
2
|
+
export const RPC_SESSION_STATE_WIDGET_KEY = "pix.session-state";
|
|
3
|
+
export const RPC_SESSION_STATE_ENV = "PIX_ACP_SESSION_STATE_BRIDGE";
|
|
4
|
+
|
|
5
|
+
export interface RpcSessionStateContext {
|
|
6
|
+
readonly mode?: unknown;
|
|
7
|
+
readonly ui?: {
|
|
8
|
+
setWidget?: (key: string, lines: string[] | undefined) => void;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Publish structured extension state through pi's supported RPC UI protocol.
|
|
14
|
+
*
|
|
15
|
+
* TUI/print/json modes keep using their native event consumers. Serialization
|
|
16
|
+
* is best-effort so an optional external UI can never break the source tool.
|
|
17
|
+
*/
|
|
18
|
+
export function publishRpcSessionState(
|
|
19
|
+
ctx: RpcSessionStateContext | undefined,
|
|
20
|
+
channel: string,
|
|
21
|
+
data: unknown,
|
|
22
|
+
): void {
|
|
23
|
+
if (
|
|
24
|
+
process.env[RPC_SESSION_STATE_ENV] !== "1"
|
|
25
|
+
|| ctx?.mode !== "rpc"
|
|
26
|
+
|| !ctx.ui?.setWidget
|
|
27
|
+
|| !channel.trim()
|
|
28
|
+
) return;
|
|
29
|
+
try {
|
|
30
|
+
ctx.ui.setWidget(RPC_SESSION_STATE_WIDGET_KEY, [channel, JSON.stringify(data)]);
|
|
31
|
+
} catch {
|
|
32
|
+
// The event bus remains authoritative when RPC UI transport is absent.
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
} from "./tool/types.js";
|
|
43
43
|
import { formatCommandTaskLine } from "./view/format.js";
|
|
44
44
|
import { formatStatusLabel } from "./view/labels.js";
|
|
45
|
+
import { publishRpcSessionState, type RpcSessionStateContext } from "../lib/rpc-session-state.js";
|
|
45
46
|
|
|
46
47
|
const SECTION_PENDING = "── Pending ──";
|
|
47
48
|
const SECTION_IN_PROGRESS = "── In Progress ──";
|
|
@@ -96,7 +97,9 @@ interface TodoToolRegistrationOptions extends TodoToolHooks {
|
|
|
96
97
|
parameters?: typeof TodoParamsSchema;
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
type TodoStateEventContext =
|
|
100
|
+
type TodoStateEventContext = RpcSessionStateContext & {
|
|
101
|
+
sessionManager?: { getSessionFile?: () => unknown; getSessionId?: () => unknown };
|
|
102
|
+
};
|
|
100
103
|
type TodoStateEventEmitter = { events?: { emit?: (channel: string, data: unknown) => void } };
|
|
101
104
|
type TodoStateEntryWriter = { appendEntry<T = unknown>(customType: string, data?: T): void };
|
|
102
105
|
|
|
@@ -225,7 +228,7 @@ export function publishTodoState(
|
|
|
225
228
|
const state = getState();
|
|
226
229
|
const sessionFile = sessionFileFromContext(ctx);
|
|
227
230
|
const sessionId = sessionIdFromContext(ctx);
|
|
228
|
-
|
|
231
|
+
const snapshot = {
|
|
229
232
|
version: 1,
|
|
230
233
|
details: {
|
|
231
234
|
action,
|
|
@@ -236,7 +239,9 @@ export function publishTodoState(
|
|
|
236
239
|
...(sessionFile ? { sessionFile } : {}),
|
|
237
240
|
...(sessionId ? { sessionId } : {}),
|
|
238
241
|
checkedAt: Date.now(),
|
|
239
|
-
}
|
|
242
|
+
};
|
|
243
|
+
pi.events?.emit?.(TODO_STATE_EVENT, snapshot);
|
|
244
|
+
publishRpcSessionState(ctx as TodoStateEventContext | undefined, TODO_STATE_EVENT, snapshot);
|
|
240
245
|
}
|
|
241
246
|
|
|
242
247
|
function appendTodoStateSnapshot(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-ui-extend",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.39",
|
|
4
4
|
"description": "Pix: a workspace-first terminal UI for Pi with tabs, readable tool activity, voice input, and bundled agent tools.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"repository": {
|
|
@@ -60,6 +60,11 @@
|
|
|
60
60
|
"test": "mise exec node@24.16.0 -- npm run test:inner",
|
|
61
61
|
"test:inner": "node --import tsx --test \"tests/**/*.test.ts\"",
|
|
62
62
|
"test:tools-suite": "npm --prefix external/pi-tools-suite test",
|
|
63
|
+
"build:acp": "npm --prefix acp run build",
|
|
64
|
+
"check:acp": "npm --prefix acp run check",
|
|
65
|
+
"dev:desktop": "npm --prefix desktop run dev",
|
|
66
|
+
"build:desktop": "npm --prefix desktop run build",
|
|
67
|
+
"check:desktop": "npm --prefix desktop run check",
|
|
63
68
|
"test:browser-qa-e2e": "npm --prefix external/pi-tools-suite run test:browser-qa-e2e",
|
|
64
69
|
"sync:pi-tools-suite": "node scripts/sync-pi-tools-suite.mjs",
|
|
65
70
|
"sync:pi-tools-suite:check": "node scripts/sync-pi-tools-suite.mjs --check",
|
|
@@ -78,9 +83,10 @@
|
|
|
78
83
|
"prepublishOnly": "npm run check && npm run build:pix && npm run generate-schemas"
|
|
79
84
|
},
|
|
80
85
|
"dependencies": {
|
|
81
|
-
"@earendil-works/pi-ai": "0.
|
|
82
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
83
|
-
"@earendil-works/pi-
|
|
86
|
+
"@earendil-works/pi-ai": "0.85.0",
|
|
87
|
+
"@earendil-works/pi-coding-agent": "0.85.0",
|
|
88
|
+
"@earendil-works/pi-server": "0.85.0",
|
|
89
|
+
"@earendil-works/pi-tui": "0.85.0",
|
|
84
90
|
"@mariozechner/clipboard": "^0.3.9",
|
|
85
91
|
"grok-mermaid": "0.2.2",
|
|
86
92
|
"jsonc-parser": "3.3.1",
|