pi-ui-extend 0.1.56 → 0.1.59
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/dist/app/app.js +2 -0
- package/dist/app/commands/command-session-actions.js +3 -2
- package/dist/app/extensions/extension-actions-controller.js +1 -3
- package/dist/app/rendering/conversation-entry-renderer.d.ts +3 -0
- package/dist/app/rendering/conversation-entry-renderer.js +2 -0
- package/dist/app/rendering/conversation-viewport.d.ts +3 -0
- package/dist/app/rendering/conversation-viewport.js +4 -0
- package/dist/app/rendering/extension-entry-renderer.d.ts +6 -0
- package/dist/app/rendering/extension-entry-renderer.js +100 -0
- package/dist/app/rendering/tool-block-renderer.d.ts +5 -0
- package/dist/app/rendering/tool-block-renderer.js +1 -1
- package/dist/app/screen/mouse-controller.js +1 -1
- package/dist/app/session/lazy-session-manager.js +18 -1
- package/dist/app/session/pix-system-message.d.ts +1 -0
- package/dist/app/session/pix-system-message.js +4 -0
- package/dist/app/session/session-event-controller.js +7 -1
- package/dist/app/session/session-history.d.ts +3 -0
- package/dist/app/session/session-history.js +19 -2
- package/dist/app/session/session-search.js +2 -0
- package/dist/app/session/session-stats.d.ts +3 -0
- package/dist/app/session/session-stats.js +65 -0
- package/dist/app/types.d.ts +6 -1
- package/dist/bundled-extensions/terminal-bell/index.js +1 -37
- package/external/pi-tools-suite/README.md +17 -1
- package/external/pi-tools-suite/docs/dcp-emergency-current-turn.md +102 -0
- package/external/pi-tools-suite/src/antigravity-auth/payload.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +0 -33
- package/external/pi-tools-suite/src/async-subagents/core/tool-guard.ts +1 -1
- package/external/pi-tools-suite/src/codex-reasoning-fix/index.ts +13 -121
- package/external/pi-tools-suite/src/dcp/config.ts +27 -0
- package/external/pi-tools-suite/src/dcp/debug-log.ts +2 -1
- package/external/pi-tools-suite/src/dcp/index.ts +193 -20
- package/external/pi-tools-suite/src/dcp/provider-tool-results.ts +85 -0
- package/external/pi-tools-suite/src/dcp/pruner-emergency.ts +254 -0
- package/external/pi-tools-suite/src/dcp/pruner-tools.ts +6 -0
- package/external/pi-tools-suite/src/dcp/pruner-types.ts +30 -0
- package/external/pi-tools-suite/src/dcp/pruner.ts +8 -0
- package/external/pi-tools-suite/src/dcp/state.ts +21 -6
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +14 -0
- package/external/pi-tools-suite/src/index.ts +4 -2
- package/external/pi-tools-suite/src/telegram-mirror/events.ts +9 -4
- package/external/pi-tools-suite/src/telegram-mirror/index.ts +1 -1
- package/external/pi-tools-suite/src/todo/index.ts +23 -27
- package/package.json +1 -1
|
@@ -17,6 +17,36 @@ export interface MessageCompressionCandidate {
|
|
|
17
17
|
reason: string;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
export interface EmergencyCurrentTurnStats {
|
|
21
|
+
totalPairs: number;
|
|
22
|
+
totalPairTokens: number;
|
|
23
|
+
eligiblePairs: number;
|
|
24
|
+
eligibleTokens: number;
|
|
25
|
+
eligibleRecoverableTokens: number;
|
|
26
|
+
preservedPairs: number;
|
|
27
|
+
preservedTokens: number;
|
|
28
|
+
preservedRecentPairs: number;
|
|
29
|
+
preservedRecentTokens: number;
|
|
30
|
+
preservedUnseenPairs: number;
|
|
31
|
+
preservedUnseenTokens: number;
|
|
32
|
+
preservedProtectedPairs: number;
|
|
33
|
+
preservedProtectedTokens: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface EmergencyCurrentTurnOutput {
|
|
37
|
+
toolCallId: string;
|
|
38
|
+
messageId?: string;
|
|
39
|
+
toolName: string;
|
|
40
|
+
tokenEstimate: number;
|
|
41
|
+
recoverableTokens: number;
|
|
42
|
+
resultIndex: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface EmergencyCurrentTurnSelection {
|
|
46
|
+
eligible: EmergencyCurrentTurnOutput[];
|
|
47
|
+
stats: EmergencyCurrentTurnStats;
|
|
48
|
+
}
|
|
49
|
+
|
|
20
50
|
export type DcpNudgeType = "context-strong" | "context-soft" | "turn" | "iteration";
|
|
21
51
|
|
|
22
52
|
export interface NudgeThresholds {
|
|
@@ -12,10 +12,18 @@ import {
|
|
|
12
12
|
|
|
13
13
|
export type {
|
|
14
14
|
CompressionCandidate,
|
|
15
|
+
EmergencyCurrentTurnSelection,
|
|
16
|
+
EmergencyCurrentTurnStats,
|
|
15
17
|
MessageCompressionCandidate,
|
|
16
18
|
MessagePriority,
|
|
17
19
|
NudgeThresholds,
|
|
18
20
|
} from "./pruner-types.js";
|
|
21
|
+
export {
|
|
22
|
+
analyzeEmergencyCurrentTurn,
|
|
23
|
+
emergencyPressureState,
|
|
24
|
+
emergencyCurrentTurnMessageCandidates,
|
|
25
|
+
pruneEmergencyCurrentTurn,
|
|
26
|
+
} from "./pruner-emergency.js";
|
|
19
27
|
export {
|
|
20
28
|
estimateTokens,
|
|
21
29
|
getActiveSummaryTokenEstimate,
|
|
@@ -145,6 +145,8 @@ export interface DcpState {
|
|
|
145
145
|
prunedToolIds: Set<string>
|
|
146
146
|
/** toolCallId → reason used for human-readable pruning placeholders/stats. */
|
|
147
147
|
prunedToolReasons: Map<string, string>
|
|
148
|
+
/** Tool results included in at least one provider request. */
|
|
149
|
+
providerSeenToolIds: Set<string>
|
|
148
150
|
|
|
149
151
|
// ── Compression ────────────────────────────────────────────────────────────
|
|
150
152
|
/** All compression blocks (both active and soft-deleted) */
|
|
@@ -228,12 +230,10 @@ export interface DcpState {
|
|
|
228
230
|
*/
|
|
229
231
|
lastContextWindow?: number
|
|
230
232
|
/**
|
|
231
|
-
* How many consecutive
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
* waiting for the model. Reset to 0 on any successful compression, when
|
|
236
|
-
* pressure drops below the emergency threshold, or on a window change.
|
|
233
|
+
* How many consecutive emergency context reminders have been delivered
|
|
234
|
+
* without a successful compression or emergency prune. This advances even
|
|
235
|
+
* when no normal compression candidate exists so current-turn fallback
|
|
236
|
+
* patience cannot be reset by the exact condition it is designed to handle.
|
|
237
237
|
*/
|
|
238
238
|
consecutiveIgnoredStrongNudges: number
|
|
239
239
|
}
|
|
@@ -248,6 +248,7 @@ export function createState(): DcpState {
|
|
|
248
248
|
toolCalls: new Map(),
|
|
249
249
|
prunedToolIds: new Set(),
|
|
250
250
|
prunedToolReasons: new Map(),
|
|
251
|
+
providerSeenToolIds: new Set(),
|
|
251
252
|
compressionBlocks: [],
|
|
252
253
|
nextBlockId: 1,
|
|
253
254
|
messageIdSnapshot: new Map(),
|
|
@@ -279,6 +280,7 @@ export function resetState(state: DcpState): void {
|
|
|
279
280
|
state.toolCalls.clear()
|
|
280
281
|
state.prunedToolIds.clear()
|
|
281
282
|
state.prunedToolReasons.clear()
|
|
283
|
+
state.providerSeenToolIds.clear()
|
|
282
284
|
state.compressionBlocks = []
|
|
283
285
|
state.nextBlockId = 1
|
|
284
286
|
state.messageIdSnapshot.clear()
|
|
@@ -401,6 +403,8 @@ export interface SerializedDcpState {
|
|
|
401
403
|
nextBlockId: number
|
|
402
404
|
prunedToolIds: string[]
|
|
403
405
|
prunedToolReasons: Array<[string, string]>
|
|
406
|
+
/** Tool result IDs known to have appeared in a provider request. */
|
|
407
|
+
providerSeenToolIds?: string[]
|
|
404
408
|
/** Full tool records — present in legacy snapshots. */
|
|
405
409
|
toolCalls?: ToolRecord[]
|
|
406
410
|
/** Compact tool records — present in new compact snapshots. */
|
|
@@ -597,11 +601,16 @@ export function serializeState(state: DcpState): SerializedDcpState {
|
|
|
597
601
|
seen.add(record.toolCallId)
|
|
598
602
|
}
|
|
599
603
|
|
|
604
|
+
const persistedToolCallIds = new Set(compactToolCalls.map((record) => record.toolCallId))
|
|
605
|
+
const providerSeenToolIds = Array.from(state.providerSeenToolIds)
|
|
606
|
+
.filter((toolCallId) => persistedToolCallIds.has(toolCallId))
|
|
607
|
+
|
|
600
608
|
return {
|
|
601
609
|
compressionBlocks: state.compressionBlocks,
|
|
602
610
|
nextBlockId: state.nextBlockId,
|
|
603
611
|
prunedToolIds: Array.from(state.prunedToolIds),
|
|
604
612
|
prunedToolReasons: Array.from(state.prunedToolReasons.entries()),
|
|
613
|
+
providerSeenToolIds,
|
|
605
614
|
compactToolCalls,
|
|
606
615
|
totalToolCallCount: allRecords.length,
|
|
607
616
|
tokensSaved: state.tokensSaved,
|
|
@@ -661,6 +670,12 @@ export function restoreState(state: DcpState, data: unknown): void {
|
|
|
661
670
|
)
|
|
662
671
|
}
|
|
663
672
|
|
|
673
|
+
if (Array.isArray(saved.providerSeenToolIds)) {
|
|
674
|
+
state.providerSeenToolIds = new Set(
|
|
675
|
+
saved.providerSeenToolIds.filter((id): id is string => typeof id === "string"),
|
|
676
|
+
)
|
|
677
|
+
}
|
|
678
|
+
|
|
664
679
|
if (Array.isArray(saved.compactToolCalls)) {
|
|
665
680
|
// New compact format: restore CompactToolRecords as ToolRecords with
|
|
666
681
|
// synthetic inputArgs derived from inputStringValues.
|
|
@@ -38,6 +38,20 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = String.raw`{
|
|
|
38
38
|
"debug": false,
|
|
39
39
|
"debugLog": { "maxBytes": 5242880, "maxBackups": 3 },
|
|
40
40
|
"manualMode": { "enabled": false, "automaticStrategies": true },
|
|
41
|
+
"strategies": {
|
|
42
|
+
"emergencyCurrentTurnPruning": {
|
|
43
|
+
// Disabling this turns off same-turn candidates and lossy pruning;
|
|
44
|
+
// non-destructive emergency reminders remain active.
|
|
45
|
+
"enabled": true,
|
|
46
|
+
"hardContextPercent": 0.82,
|
|
47
|
+
"targetContextPercent": 0.70,
|
|
48
|
+
"patience": 2,
|
|
49
|
+
"keepRecentToolPairs": 8,
|
|
50
|
+
"minOutputTokens": 500,
|
|
51
|
+
"maxSuggestions": 8,
|
|
52
|
+
"protectedTools": []
|
|
53
|
+
}
|
|
54
|
+
},
|
|
41
55
|
"modelOverrides": {
|
|
42
56
|
"openai-codex/gpt-5*": {
|
|
43
57
|
"compress": {
|
|
@@ -9,8 +9,7 @@ type ExtensionModule = {
|
|
|
9
9
|
default: ExtensionFactory;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
const MODULES: Array<{ name: string; load: () => Promise<ExtensionModule> }> = [
|
|
13
|
-
{ name: "codex-reasoning-fix", load: () => import("./codex-reasoning-fix/index") },
|
|
12
|
+
export const MODULES: Array<{ name: string; load: () => Promise<ExtensionModule> }> = [
|
|
14
13
|
{ name: "coding-discipline", load: () => import("./coding-discipline/index") },
|
|
15
14
|
{ name: "ast-grep", load: () => import("./ast-grep/index") },
|
|
16
15
|
{ name: "async-subagents", load: () => import("./async-subagents/index") },
|
|
@@ -28,6 +27,9 @@ const MODULES: Array<{ name: string; load: () => Promise<ExtensionModule> }> = [
|
|
|
28
27
|
{ name: "prompt-commands", load: () => import("./prompt-commands/index") },
|
|
29
28
|
{ name: "skill-installer", load: () => import("./skill-installer/index") },
|
|
30
29
|
{ name: "telegram-mirror", load: () => import("./telegram-mirror/index") },
|
|
30
|
+
// Keep this last: its before_provider_request handler is the final payload
|
|
31
|
+
// sanitizer after DCP and any other provider-payload modifiers.
|
|
32
|
+
{ name: "codex-reasoning-fix", load: () => import("./codex-reasoning-fix/index") },
|
|
31
33
|
];
|
|
32
34
|
|
|
33
35
|
export default async function piToolsSuite(pi: ExtensionAPI) {
|
|
@@ -18,7 +18,11 @@ export interface RendererSink {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export function registerPixEventHandlers(pi: ExtensionAPI, hooks: PixMirrorHooks): void {
|
|
21
|
+
let turnActive = false;
|
|
22
|
+
|
|
21
23
|
pi.on("agent_start", (_event, ctx) => {
|
|
24
|
+
if (turnActive) return;
|
|
25
|
+
turnActive = true;
|
|
22
26
|
hooks.getRenderer()?.push({ kind: "turn_start", instance: hooks.describeInstance(ctx as ExtensionContext | undefined) });
|
|
23
27
|
});
|
|
24
28
|
|
|
@@ -33,16 +37,18 @@ export function registerPixEventHandlers(pi: ExtensionAPI, hooks: PixMirrorHooks
|
|
|
33
37
|
// user-visible assistant answer, not internal reasoning/tools.
|
|
34
38
|
});
|
|
35
39
|
|
|
36
|
-
pi.on("
|
|
40
|
+
pi.on("agent_settled", () => {
|
|
41
|
+
if (!turnActive) return;
|
|
42
|
+
turnActive = false;
|
|
37
43
|
hooks.getRenderer()?.push({ kind: "turn_end", reason: "end" });
|
|
38
|
-
hooks.
|
|
44
|
+
hooks.notifyAgentSettled();
|
|
39
45
|
});
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
export interface PixMirrorHooks {
|
|
43
49
|
getRenderer(): RendererSink | undefined;
|
|
44
50
|
describeInstance(ctx: ExtensionContext | undefined): RendererInstance | undefined;
|
|
45
|
-
|
|
51
|
+
notifyAgentSettled(): void;
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
export function captureAbortableContext(ctx: ExtensionContext | undefined, hooks: ContextCapture): void {
|
|
@@ -59,4 +65,3 @@ export interface ContextCapture {
|
|
|
59
65
|
capturePending(fn: () => boolean): void;
|
|
60
66
|
captureCompact(fn: () => void): void;
|
|
61
67
|
}
|
|
62
|
-
|
|
@@ -166,7 +166,7 @@ export default function telegramMirror(pi: ExtensionAPI): void {
|
|
|
166
166
|
const hooks: PixMirrorHooks = {
|
|
167
167
|
getRenderer: () => eventSink,
|
|
168
168
|
describeInstance: (ctx) => describeInstance(ctx),
|
|
169
|
-
|
|
169
|
+
notifyAgentSettled: () => undefined,
|
|
170
170
|
};
|
|
171
171
|
|
|
172
172
|
registerPixEventHandlers(pi, hooks);
|
|
@@ -10,16 +10,6 @@ import { getState, replaceState } from "./state/store.js";
|
|
|
10
10
|
import { activateTodoStateScope, DEFAULT_PROMPT_GUIDELINES, DEFAULT_PROMPT_SNIPPET, publishTodoState, registerTodosCommand, registerTodoTool } from "./todo.js";
|
|
11
11
|
import type { Task, TaskMutationParams } from "./tool/types.js";
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* Renderer-relayed signal that the session is in an auto-retry cycle.
|
|
15
|
-
* Payload: `{ active: boolean }`. The SDK does not forward retry state to
|
|
16
|
-
* extensions, so the renderer emits this on the extension event bus. We use it
|
|
17
|
-
* to suppress the auto-nudge on intermediate retry agent_end events: nudging
|
|
18
|
-
* then races the pending retry continuation and surfaces a benign
|
|
19
|
-
* "Agent is already processing" extension error in other tabs.
|
|
20
|
-
*/
|
|
21
|
-
const RETRY_ACTIVE_EVENT = "pix:retry-active";
|
|
22
|
-
|
|
23
13
|
type AgentMessageLike = { role?: unknown; stopReason?: unknown; content?: unknown };
|
|
24
14
|
|
|
25
15
|
const TODO_NUDGE_LIMIT = 8;
|
|
@@ -144,10 +134,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
144
134
|
const pendingAskUserToolCallIds = new Set<string>();
|
|
145
135
|
let suppressNextNudgeForThinkingSwitch = false;
|
|
146
136
|
let inProgressAtAgentStart = new Set<number>();
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
let
|
|
137
|
+
// agent_end may be followed by an automatic retry, compaction, or queued
|
|
138
|
+
// continuation. Remember only whether its terminal assistant reply was a
|
|
139
|
+
// successful visible response; schedule a nudge later on agent_settled.
|
|
140
|
+
let settledNudgeEligible = false;
|
|
151
141
|
|
|
152
142
|
function registerTodoToolWithCurrentPrompt(): void {
|
|
153
143
|
const thinkingPrompt = todoThinkingEnabled ? buildThinkingPromptParts(currentModel) : {};
|
|
@@ -336,9 +326,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
336
326
|
const delayMs = attempt === 0 ? TODO_NUDGE_INITIAL_DELAY_MS : TODO_NUDGE_IDLE_RETRY_DELAY_MS;
|
|
337
327
|
nudgeTimer = setTimeout(() => {
|
|
338
328
|
nudgeTimer = undefined;
|
|
339
|
-
// A retry-start signal may have arrived after the agent_end that
|
|
340
|
-
// scheduled this nudge. Bail out so we don't nudge mid-retry.
|
|
341
|
-
if (retryActive) return;
|
|
342
329
|
try {
|
|
343
330
|
activateTodoStateScope(ctx);
|
|
344
331
|
if (!ctx.isIdle()) {
|
|
@@ -358,9 +345,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
358
345
|
if (nudge.signature === lastNudgedSignature) return;
|
|
359
346
|
lastNudgedSignature = nudge.signature;
|
|
360
347
|
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
// queueing followUp from inside agent_end can be too late to be drained.
|
|
348
|
+
// agent_settled means retries, compaction, and queued continuations are
|
|
349
|
+
// finished. Send on the next idle tick as a fresh turn.
|
|
364
350
|
pi.sendUserMessage(nudge.message);
|
|
365
351
|
} catch (err) {
|
|
366
352
|
if (isAgentBusyRaceError(err)) {
|
|
@@ -413,12 +399,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
413
399
|
clearNudgeTimer();
|
|
414
400
|
});
|
|
415
401
|
|
|
416
|
-
pi.events.on(RETRY_ACTIVE_EVENT, (data: unknown) => {
|
|
417
|
-
const active = data != null && typeof data === "object" && (data as { active?: unknown }).active === true;
|
|
418
|
-
retryActive = active;
|
|
419
|
-
if (active) clearNudgeTimer();
|
|
420
|
-
});
|
|
421
|
-
|
|
422
402
|
pi.on("model_select", async (event) => {
|
|
423
403
|
currentModel = event.model;
|
|
424
404
|
if (todoThinkingEnabled) registerTodoToolWithCurrentPrompt();
|
|
@@ -439,7 +419,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
439
419
|
pi.on("agent_start", async (_event, ctx) => {
|
|
440
420
|
activateTodoStateScope(ctx);
|
|
441
421
|
pendingAskUserToolCallIds.clear();
|
|
442
|
-
|
|
422
|
+
settledNudgeEligible = false;
|
|
443
423
|
inProgressAtAgentStart = new Set(selectVisibleTasks(getState()).filter((task) => task.status === "in_progress").map((task) => task.id));
|
|
444
424
|
});
|
|
445
425
|
|
|
@@ -455,26 +435,42 @@ export default function (pi: ExtensionAPI) {
|
|
|
455
435
|
pi.on("agent_end", async (event, ctx) => {
|
|
456
436
|
activateTodoStateScope(ctx);
|
|
457
437
|
const completedAssistantReply = hasCompletedAssistantReply((event as { messages?: readonly unknown[] } | undefined)?.messages);
|
|
438
|
+
settledNudgeEligible = completedAssistantReply;
|
|
458
439
|
|
|
459
440
|
if (suppressNextNudgeForThinkingSwitch) {
|
|
460
441
|
suppressNextNudgeForThinkingSwitch = false;
|
|
461
442
|
if (!completedAssistantReply) {
|
|
443
|
+
settledNudgeEligible = false;
|
|
462
444
|
clearNudgeTimer();
|
|
463
445
|
return;
|
|
464
446
|
}
|
|
465
447
|
}
|
|
466
448
|
|
|
467
449
|
if (pendingAskUserToolCallIds.size > 0) {
|
|
450
|
+
settledNudgeEligible = false;
|
|
468
451
|
clearNudgeTimer();
|
|
469
452
|
return;
|
|
470
453
|
}
|
|
471
454
|
|
|
472
455
|
if (completedAssistantReply && maybeRecoverCompletedCurrentTask((event as { messages?: readonly unknown[] } | undefined)?.messages, ctx)) {
|
|
456
|
+
settledNudgeEligible = false;
|
|
473
457
|
lastNudgedSignature = undefined;
|
|
474
458
|
clearNudgeTimer();
|
|
475
459
|
return;
|
|
476
460
|
}
|
|
461
|
+
});
|
|
477
462
|
|
|
463
|
+
pi.on("agent_settled", async (_event, ctx) => {
|
|
464
|
+
activateTodoStateScope(ctx);
|
|
465
|
+
if (!settledNudgeEligible) {
|
|
466
|
+
clearNudgeTimer();
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
settledNudgeEligible = false;
|
|
470
|
+
if (pendingAskUserToolCallIds.size > 0) {
|
|
471
|
+
clearNudgeTimer();
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
478
474
|
const nudge = getUnfinishedTodoNudge();
|
|
479
475
|
if (!nudge) {
|
|
480
476
|
lastNudgedSignature = undefined;
|