pi-ui-extend 1.0.41 → 1.0.45
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 +50 -13
- package/dist/app/app.d.ts +13 -0
- package/dist/app/app.js +158 -8
- package/dist/app/cli/install.js +2 -2
- package/dist/app/commands/command-controller.d.ts +4 -0
- package/dist/app/commands/command-controller.js +11 -0
- package/dist/app/commands/command-git-actions.d.ts +25 -0
- package/dist/app/commands/command-git-actions.js +381 -0
- package/dist/app/commands/command-host.d.ts +4 -0
- package/dist/app/commands/command-host.js +5 -2
- package/dist/app/commands/command-model-actions.d.ts +1 -0
- package/dist/app/commands/command-model-actions.js +57 -35
- package/dist/app/commands/command-navigation-actions.d.ts +1 -0
- package/dist/app/commands/command-navigation-actions.js +16 -3
- package/dist/app/commands/command-registry.d.ts +2 -0
- package/dist/app/commands/command-registry.js +15 -1
- package/dist/app/commands/command-session-actions.js +9 -3
- package/dist/app/commands/reload-context-inventory.d.ts +11 -0
- package/dist/app/commands/reload-context-inventory.js +70 -0
- package/dist/app/extensions/extension-actions-controller.d.ts +1 -0
- package/dist/app/extensions/extension-actions-controller.js +6 -0
- package/dist/app/extensions/subagent-catalog-state.d.ts +9 -0
- package/dist/app/extensions/subagent-catalog-state.js +23 -0
- package/dist/app/input/autocomplete-controller.js +61 -34
- package/dist/app/input/input-action-controller.d.ts +2 -0
- package/dist/app/input/input-action-controller.js +8 -1
- package/dist/app/input/input-controller.d.ts +2 -1
- package/dist/app/input/input-controller.js +7 -2
- package/dist/app/input/prompt-enhancer-controller.js +53 -35
- package/dist/app/input/voice-controller.d.ts +41 -45
- package/dist/app/input/voice-controller.js +351 -419
- package/dist/app/model/model-usage-controller.js +17 -7
- package/dist/app/model/model-usage-status.d.ts +4 -1
- package/dist/app/model/model-usage-status.js +270 -46
- package/dist/app/popup/menu-items-controller.d.ts +13 -3
- package/dist/app/popup/menu-items-controller.js +37 -21
- package/dist/app/popup/popup-action-controller.d.ts +12 -2
- package/dist/app/popup/popup-action-controller.js +77 -24
- package/dist/app/popup/popup-menu-controller.d.ts +36 -14
- package/dist/app/popup/popup-menu-controller.js +239 -69
- package/dist/app/rendering/dcp-stats.d.ts +9 -4
- package/dist/app/rendering/dcp-stats.js +40 -425
- package/dist/app/rendering/editor-panels.js +10 -4
- package/dist/app/rendering/popup-menu-renderer.d.ts +3 -5
- package/dist/app/rendering/popup-menu-renderer.js +42 -37
- package/dist/app/rendering/render-controller.js +23 -2
- package/dist/app/rendering/status-line-renderer.d.ts +5 -0
- package/dist/app/rendering/status-line-renderer.js +41 -5
- package/dist/app/rendering/tab-line-renderer.js +26 -20
- package/dist/app/runtime.d.ts +12 -1
- package/dist/app/runtime.js +120 -12
- package/dist/app/screen/mouse-controller.d.ts +2 -0
- package/dist/app/screen/mouse-controller.js +17 -7
- package/dist/app/screen/status-controller.d.ts +4 -0
- package/dist/app/screen/status-controller.js +5 -0
- package/dist/app/session/lazy-session-manager.js +34 -0
- package/dist/app/session/session-event-controller.d.ts +1 -0
- package/dist/app/session/session-event-controller.js +10 -1
- package/dist/app/session/session-history.d.ts +1 -0
- package/dist/app/session/session-history.js +12 -1
- package/dist/app/session/session-lifecycle-controller.d.ts +7 -1
- package/dist/app/session/session-lifecycle-controller.js +16 -1
- package/dist/app/session/tabs-controller.d.ts +26 -1
- package/dist/app/session/tabs-controller.js +454 -147
- package/dist/app/subagents/subagents-files.js +60 -1
- package/dist/app/subagents/subagents-model.d.ts +1 -0
- package/dist/app/subagents/subagents-model.js +18 -2
- package/dist/app/subagents/subagents-widget-controller.d.ts +1 -0
- package/dist/app/subagents/subagents-widget-controller.js +6 -0
- package/dist/app/types.d.ts +16 -1
- package/dist/app/workspace/workspace-actions-controller.js +10 -2
- package/dist/app/workspace/workspace-undo.d.ts +1 -0
- package/dist/app/workspace/workspace-undo.js +1 -0
- package/dist/bundled-extensions/question/index.js +9 -1
- package/dist/bundled-extensions/question/remote.d.ts +4 -0
- package/dist/bundled-extensions/question/remote.js +33 -0
- package/dist/bundled-extensions/telegram-connector/bot.d.ts +43 -0
- package/dist/bundled-extensions/telegram-connector/bot.js +166 -0
- package/dist/bundled-extensions/telegram-connector/config.d.ts +8 -0
- package/dist/bundled-extensions/telegram-connector/config.js +87 -0
- package/dist/bundled-extensions/telegram-connector/coordinator.d.ts +66 -0
- package/dist/bundled-extensions/telegram-connector/coordinator.js +413 -0
- package/dist/bundled-extensions/telegram-connector/index.d.ts +3 -0
- package/dist/bundled-extensions/telegram-connector/index.js +195 -0
- package/dist/bundled-extensions/terminal-bell/index.d.ts +0 -8
- package/dist/bundled-extensions/terminal-bell/index.js +0 -76
- package/dist/bundled-extensions/workspace-undo/index.d.ts +26 -0
- package/dist/bundled-extensions/workspace-undo/index.js +191 -0
- package/dist/config.d.ts +21 -2
- package/dist/config.js +159 -32
- package/dist/default-pix-config.js +25 -5
- package/dist/schemas/index.d.ts +1 -0
- package/dist/schemas/index.js +1 -0
- package/dist/schemas/pi-tools-suite-schema.d.ts +88 -62
- package/dist/schemas/pi-tools-suite-schema.js +54 -83
- package/dist/schemas/pix-schema.d.ts +19 -2
- package/dist/schemas/pix-schema.js +47 -5
- package/dist/schemas/tasks-schema.d.ts +18 -0
- package/dist/schemas/tasks-schema.js +41 -0
- package/docs/concurrency.md +9 -1
- package/docs/desktop-mvp.md +22 -6
- package/docs/desktop-task-manager.md +148 -84
- package/docs/release.md +30 -6
- package/external/pi-tools-suite/README.md +197 -84
- package/external/pi-tools-suite/docs/evals.md +1 -1
- package/external/pi-tools-suite/docs/session-recovery.md +47 -14
- package/external/pi-tools-suite/docs/subagent-model-pools.md +48 -30
- package/external/pi-tools-suite/docs/ui-qa-subagent.md +440 -0
- package/external/pi-tools-suite/package.json +6 -1
- package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +2 -1
- package/external/pi-tools-suite/src/antigravity-auth/constants.ts +9 -3
- package/external/pi-tools-suite/src/antigravity-auth/headers.ts +41 -3
- package/external/pi-tools-suite/src/antigravity-auth/models.ts +94 -21
- package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +5 -17
- package/external/pi-tools-suite/src/antigravity-auth/payload.ts +72 -7
- package/external/pi-tools-suite/src/antigravity-auth/stream.ts +13 -1
- package/external/pi-tools-suite/src/async-subagents/agents/frontier-review.md +23 -0
- package/external/pi-tools-suite/src/async-subagents/agents/implement.md +1 -1
- package/external/pi-tools-suite/src/async-subagents/agents/presets.jsonc +16 -0
- package/external/pi-tools-suite/src/async-subagents/agents/research.md +5 -3
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/browser.mjs +346 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/desktop.mjs +1038 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/tui.mjs +759 -0
- package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/scripts/browser-qa-runner.mjs +32 -23
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/chrome-devtools/chrome-devtools-provider.mjs +895 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/linux/linux-atspi.py +494 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/macos/macos-accessibility.swift +1085 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/bridge-client.mjs +50 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/native-terminal-host.mjs +501 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/windows/windows-uia.ps1 +454 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-auth.md +114 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-chrome-devtools.md +91 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-playwright.md +145 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser.md +82 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-linux.md +26 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-macos.md +29 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-windows.md +23 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop.md +54 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-native-terminal.md +59 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-pty.md +37 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui.md +60 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/scripts/ui-qa-runner.mjs +535 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa.md +75 -0
- package/external/pi-tools-suite/src/async-subagents/commands.ts +15 -71
- package/external/pi-tools-suite/src/async-subagents/core/activity.ts +33 -0
- package/external/pi-tools-suite/src/async-subagents/core/agent-catalog.ts +5 -4
- package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +8 -5
- package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +16 -2
- package/external/pi-tools-suite/src/async-subagents/core/config.ts +160 -327
- package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +2 -1
- package/external/pi-tools-suite/src/async-subagents/core/registry.ts +158 -33
- package/external/pi-tools-suite/src/async-subagents/core/routing.ts +21 -5
- package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +40 -26
- package/external/pi-tools-suite/src/async-subagents/core/state.ts +40 -0
- package/external/pi-tools-suite/src/async-subagents/core/types.ts +7 -0
- package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +51 -45
- package/external/pi-tools-suite/src/async-subagents/index.ts +69 -5
- package/external/pi-tools-suite/src/async-subagents/lib.ts +16 -9
- package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +8 -5
- package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +5 -4
- package/external/pi-tools-suite/src/async-subagents/types.ts +1 -0
- package/external/pi-tools-suite/src/coding-discipline/index.ts +90 -59
- package/external/pi-tools-suite/src/config.ts +55 -1
- package/external/pi-tools-suite/src/context-gateway/accounting-log.ts +282 -0
- package/external/pi-tools-suite/src/context-gateway/config.ts +77 -3
- package/external/pi-tools-suite/src/context-gateway/efficiency.ts +414 -0
- package/external/pi-tools-suite/src/context-gateway/enforcement.ts +222 -0
- package/external/pi-tools-suite/src/context-gateway/index.ts +206 -19
- package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +7 -7
- package/external/pi-tools-suite/src/context-gateway/telemetry.ts +101 -28
- package/external/pi-tools-suite/src/context-gateway/types.ts +16 -3
- package/external/pi-tools-suite/src/context-inventory.ts +99 -0
- package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +39 -4
- package/external/pi-tools-suite/src/dcp/auto-compress.ts +128 -49
- package/external/pi-tools-suite/src/dcp/commands.ts +32 -156
- package/external/pi-tools-suite/src/dcp/compress-tool.ts +177 -46
- package/external/pi-tools-suite/src/dcp/compression-blocks.ts +6 -58
- package/external/pi-tools-suite/src/dcp/compression-preview.ts +9 -0
- package/external/pi-tools-suite/src/dcp/compression-progress.ts +22 -0
- package/external/pi-tools-suite/src/dcp/config.ts +114 -17
- package/external/pi-tools-suite/src/dcp/conversation-index.ts +36 -9
- package/external/pi-tools-suite/src/dcp/diagnostics.ts +41 -0
- package/external/pi-tools-suite/src/dcp/fresh-tool-results.ts +38 -0
- package/external/pi-tools-suite/src/dcp/index.ts +338 -66
- package/external/pi-tools-suite/src/dcp/journal.ts +126 -4
- package/external/pi-tools-suite/src/dcp/progress-controller.ts +2 -1
- package/external/pi-tools-suite/src/dcp/prompts.ts +83 -192
- package/external/pi-tools-suite/src/dcp/protected-continuity.ts +175 -0
- package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +93 -3
- package/external/pi-tools-suite/src/dcp/pruner-message-ids.ts +6 -10
- package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +36 -33
- package/external/pi-tools-suite/src/dcp/pruner-tools.ts +6 -3
- package/external/pi-tools-suite/src/dcp/pruner.ts +1 -0
- package/external/pi-tools-suite/src/dcp/routine-pressure.ts +86 -0
- package/external/pi-tools-suite/src/dcp/state.ts +9 -0
- package/external/pi-tools-suite/src/dcp/statistics.d.ts +8 -0
- package/external/pi-tools-suite/src/dcp/statistics.js +156 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +78 -22
- package/external/pi-tools-suite/src/index.ts +7 -1
- package/external/pi-tools-suite/src/lib/project.ts +36 -1
- package/external/pi-tools-suite/src/model-tools/index.ts +10 -7
- package/external/pi-tools-suite/src/repo-discovery/index.ts +304 -4
- package/external/pi-tools-suite/src/resource-registry/index.ts +2551 -0
- package/external/pi-tools-suite/src/session-recovery/index.ts +17 -0
- package/external/pi-tools-suite/src/shell-command-policy.ts +219 -0
- package/external/pi-tools-suite/src/todo/index.ts +21 -0
- package/external/pi-tools-suite/src/todo/todo.ts +19 -1
- package/external/pi-tools-suite/src/tool-descriptions.ts +47 -19
- package/package.json +10 -9
- package/schemas/pi-tools-suite.json +466 -287
- package/schemas/pix.json +129 -11
- package/schemas/tasks.json +131 -0
- package/skills/simplify/SKILL.md +33 -5
- package/docs/desktop-markdown-media.md +0 -77
- package/external/pi-tools-suite/docs/browser-qa-subagent.md +0 -177
- package/external/pi-tools-suite/docs/dcp-emergency-current-turn.md +0 -102
- package/external/pi-tools-suite/src/async-subagents/agents/browser-qa.md +0 -598
- package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +0 -54
- package/external/pi-tools-suite/src/skill-installer/index.ts +0 -333
- package/skills/playwright-cli/SKILL.md +0 -420
- package/skills/playwright-cli/references/element-attributes.md +0 -23
- package/skills/playwright-cli/references/playwright-tests.md +0 -50
- package/skills/playwright-cli/references/request-mocking.md +0 -87
- package/skills/playwright-cli/references/running-code.md +0 -241
- package/skills/playwright-cli/references/session-management.md +0 -273
- package/skills/playwright-cli/references/spec-driven-testing.md +0 -311
- package/skills/playwright-cli/references/storage-state.md +0 -290
- package/skills/playwright-cli/references/test-generation.md +0 -142
- package/skills/playwright-cli/references/tracing.md +0 -154
- package/skills/playwright-cli/references/video-recording.md +0 -147
- package/skills/spec-lite/SKILL.md +0 -140
- /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-auth.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-flow.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.LICENSE +0 -0
- /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.mjs +0 -0
|
@@ -27,6 +27,28 @@ export function outstandingCompressionTokens(state: DcpState, input: Compression
|
|
|
27
27
|
return Math.max(0, previous.remainingTokens + growth + stricterTarget)
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Bound one automatic recovery plan to what the *current* budget actually
|
|
32
|
+
* requires. `compressionProgress.remainingTokens` is deliberately cumulative:
|
|
33
|
+
* it follows content growth and stricter historical targets across callbacks.
|
|
34
|
+
* That debt is useful for progress accounting, but feeding the whole stale debt
|
|
35
|
+
* back into candidate sizing can make a later, much smaller capacity gap select
|
|
36
|
+
* an uneconomically huge range (and repeatedly fail with non-positive gain).
|
|
37
|
+
*/
|
|
38
|
+
export function compressionPlanningTokens(
|
|
39
|
+
state: DcpState,
|
|
40
|
+
input: CompressionProgressInput,
|
|
41
|
+
remainingTokens: number | undefined = outstandingCompressionTokens(state, input),
|
|
42
|
+
): number {
|
|
43
|
+
const requested = Math.max(1, Math.ceil(input.requiredTokens))
|
|
44
|
+
if (
|
|
45
|
+
remainingTokens === undefined ||
|
|
46
|
+
remainingTokens <= 0 ||
|
|
47
|
+
state.compressionProgress?.kind !== input.kind
|
|
48
|
+
) return requested
|
|
49
|
+
return Math.min(remainingTokens, requested)
|
|
50
|
+
}
|
|
51
|
+
|
|
30
52
|
/** Follow actual projection growth, not context callback/retry count. */
|
|
31
53
|
export function trackCompressionProgress(
|
|
32
54
|
state: DcpState,
|
|
@@ -10,6 +10,8 @@ import { parse as parseJsonc, type ParseError } from "jsonc-parser"
|
|
|
10
10
|
export interface DcpConfig {
|
|
11
11
|
enabled: boolean
|
|
12
12
|
debug: boolean
|
|
13
|
+
/** Non-fatal loader diagnostics, e.g. removed settings that were ignored. */
|
|
14
|
+
issues?: string[]
|
|
13
15
|
debugLog?: {
|
|
14
16
|
maxBytes?: number
|
|
15
17
|
maxBackups?: number
|
|
@@ -55,15 +57,21 @@ export interface DcpConfig {
|
|
|
55
57
|
* session — disabled by default; opt in via config.
|
|
56
58
|
*/
|
|
57
59
|
autoCompress: {
|
|
60
|
+
/** Gates autonomous firing only. Explicit `compress` calls may still use
|
|
61
|
+
* the summarizer settings below when their per-selection summary is omitted. */
|
|
58
62
|
enabled: boolean
|
|
59
63
|
/** Auto fallback is eligible after more than this many completed,
|
|
60
64
|
* correlated opportunities with a delivered actionable reminder.
|
|
61
65
|
* Failed calls and insufficient commits do not reset patience. */
|
|
62
66
|
patience: number
|
|
63
|
-
/** Models to try, in order,
|
|
67
|
+
/** Models to try, in order, for auto-compress and explicit generated summaries.
|
|
68
|
+
* The first entry is the primary summarizer. Additional entries remain
|
|
69
|
+
* supported for backward compatibility with the old inline fallback list.
|
|
64
70
|
* Empty array → deterministic programmatic digest (no model call). */
|
|
65
71
|
summarizerModel: string[]
|
|
66
|
-
/**
|
|
72
|
+
/** Explicit fallback summarizers tried after `summarizerModel`, in order. */
|
|
73
|
+
summarizerFallbackModels: string[]
|
|
74
|
+
/** Hard ceiling in ms for a summarizer model call on either path. */
|
|
67
75
|
timeoutMs: number
|
|
68
76
|
}
|
|
69
77
|
}
|
|
@@ -90,7 +98,7 @@ export interface DcpConfig {
|
|
|
90
98
|
modelOverrides: Record<string, DcpConfigOverride>
|
|
91
99
|
}
|
|
92
100
|
|
|
93
|
-
export type DcpConfigOverride = DeepPartial<Omit<DcpConfig, "modelOverrides">>
|
|
101
|
+
export type DcpConfigOverride = DeepPartial<Omit<DcpConfig, "modelOverrides" | "issues">>
|
|
94
102
|
|
|
95
103
|
type DeepPartial<T> = T extends Array<infer U>
|
|
96
104
|
? Array<DeepPartial<U>>
|
|
@@ -105,6 +113,7 @@ type DeepPartial<T> = T extends Array<infer U>
|
|
|
105
113
|
const DEFAULT_CONFIG: DcpConfig = {
|
|
106
114
|
enabled: true,
|
|
107
115
|
debug: false,
|
|
116
|
+
issues: [],
|
|
108
117
|
manualMode: {
|
|
109
118
|
enabled: false,
|
|
110
119
|
},
|
|
@@ -139,6 +148,7 @@ const DEFAULT_CONFIG: DcpConfig = {
|
|
|
139
148
|
enabled: false,
|
|
140
149
|
patience: 2,
|
|
141
150
|
summarizerModel: [],
|
|
151
|
+
summarizerFallbackModels: [],
|
|
142
152
|
timeoutMs: 20000,
|
|
143
153
|
},
|
|
144
154
|
},
|
|
@@ -228,43 +238,75 @@ function readJsoncFile(filePath: string): Record<string, unknown> {
|
|
|
228
238
|
/**
|
|
229
239
|
* Return the nested DCP config from a shared pi-tools-suite config file.
|
|
230
240
|
*/
|
|
231
|
-
function readDcpFromSuiteConfig(filePath: string): Record<string, unknown> {
|
|
241
|
+
function readDcpFromSuiteConfig(filePath: string, issues: string[]): Record<string, unknown> {
|
|
232
242
|
const raw = readJsoncFile(filePath)
|
|
233
243
|
const dcp = raw["dcp"]
|
|
234
244
|
if (dcp === null || typeof dcp !== "object" || Array.isArray(dcp)) return {}
|
|
235
|
-
return stripRemovedDcpKeys(dcp as Record<string, unknown
|
|
245
|
+
return stripRemovedDcpKeys(dcp as Record<string, unknown>, issues)
|
|
236
246
|
}
|
|
237
247
|
|
|
238
|
-
|
|
248
|
+
const REMOVED_DCP_GUIDANCE: Record<string, string> = {
|
|
249
|
+
pruneNotification: "removed; pruning notifications are no longer configurable",
|
|
250
|
+
"manualMode.automaticStrategies": "removed; manual mode now has one explicit enabled flag",
|
|
251
|
+
"strategies.deduplication": "removed; no automatic replacement policy is currently mapped to this setting",
|
|
252
|
+
"strategies.purgeErrors": "removed; no automatic replacement policy is currently mapped to this setting",
|
|
253
|
+
"strategies.autoToolPruning": "removed; use explicit /dcp sweep or DCP compression/emergency policies instead",
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function reportRemovedKey(issues: string[], prefix: string, key: string): void {
|
|
257
|
+
const fullKey = prefix ? `${prefix}.${key}` : key
|
|
258
|
+
const canonical = fullKey.replace(/^modelOverrides\.[^.]+\./, "")
|
|
259
|
+
const guidance = REMOVED_DCP_GUIDANCE[canonical] ?? "removed and ignored"
|
|
260
|
+
issues.push(`dcp.${fullKey} is ${guidance}.`)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function stripRemovedDcpKeys(
|
|
264
|
+
raw: Record<string, unknown>,
|
|
265
|
+
issues: string[],
|
|
266
|
+
prefix = "",
|
|
267
|
+
): Record<string, unknown> {
|
|
239
268
|
const cleaned = structuredClone(raw)
|
|
269
|
+
if (Object.prototype.hasOwnProperty.call(cleaned, "pruneNotification")) {
|
|
270
|
+
reportRemovedKey(issues, prefix, "pruneNotification")
|
|
271
|
+
}
|
|
240
272
|
delete cleaned.pruneNotification
|
|
241
273
|
|
|
242
274
|
const manualMode = cleaned.manualMode
|
|
243
275
|
if (manualMode && typeof manualMode === "object" && !Array.isArray(manualMode)) {
|
|
276
|
+
if (Object.prototype.hasOwnProperty.call(manualMode, "automaticStrategies")) {
|
|
277
|
+
reportRemovedKey(issues, prefix, "manualMode.automaticStrategies")
|
|
278
|
+
}
|
|
244
279
|
delete (manualMode as Record<string, unknown>).automaticStrategies
|
|
245
280
|
}
|
|
246
281
|
|
|
247
282
|
const strategies = cleaned.strategies
|
|
248
283
|
if (strategies && typeof strategies === "object" && !Array.isArray(strategies)) {
|
|
249
284
|
const record = strategies as Record<string, unknown>
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
285
|
+
for (const key of ["deduplication", "purgeErrors", "autoToolPruning"] as const) {
|
|
286
|
+
if (Object.prototype.hasOwnProperty.call(record, key)) {
|
|
287
|
+
reportRemovedKey(issues, prefix, `strategies.${key}`)
|
|
288
|
+
}
|
|
289
|
+
delete record[key]
|
|
290
|
+
}
|
|
253
291
|
}
|
|
254
292
|
|
|
255
293
|
const modelOverrides = cleaned.modelOverrides
|
|
256
294
|
if (modelOverrides && typeof modelOverrides === "object" && !Array.isArray(modelOverrides)) {
|
|
257
295
|
for (const [key, override] of Object.entries(modelOverrides as Record<string, unknown>)) {
|
|
258
296
|
if (!override || typeof override !== "object" || Array.isArray(override)) continue
|
|
259
|
-
;(modelOverrides as Record<string, unknown>)[key] = stripRemovedDcpKeys(
|
|
297
|
+
;(modelOverrides as Record<string, unknown>)[key] = stripRemovedDcpKeys(
|
|
298
|
+
override as Record<string, unknown>,
|
|
299
|
+
issues,
|
|
300
|
+
prefix ? `${prefix}.modelOverrides.${key}` : `modelOverrides.${key}`,
|
|
301
|
+
)
|
|
260
302
|
}
|
|
261
303
|
}
|
|
262
304
|
|
|
263
305
|
return cleaned
|
|
264
306
|
}
|
|
265
307
|
|
|
266
|
-
function mergeSuiteDcpConfig(config: DcpConfig, filePath: string): DcpConfig {
|
|
267
|
-
const raw = readDcpFromSuiteConfig(filePath)
|
|
308
|
+
function mergeSuiteDcpConfig(config: DcpConfig, filePath: string, issues: string[]): DcpConfig {
|
|
309
|
+
const raw = readDcpFromSuiteConfig(filePath, issues)
|
|
268
310
|
if (Object.keys(raw).length === 0) return config
|
|
269
311
|
return deepMerge(config, raw as Partial<DcpConfig>)
|
|
270
312
|
}
|
|
@@ -275,6 +317,55 @@ function normalizeModelKey(key: string | undefined): string | undefined {
|
|
|
275
317
|
return trimmed.length > 0 ? trimmed : undefined
|
|
276
318
|
}
|
|
277
319
|
|
|
320
|
+
export function summarizerModelRefs(
|
|
321
|
+
settings: DcpConfig["compress"]["autoCompress"],
|
|
322
|
+
): string[] {
|
|
323
|
+
const refs = [
|
|
324
|
+
...(Array.isArray(settings.summarizerModel) ? settings.summarizerModel : []),
|
|
325
|
+
...(Array.isArray(settings.summarizerFallbackModels) ? settings.summarizerFallbackModels : []),
|
|
326
|
+
]
|
|
327
|
+
const seen = new Set<string>()
|
|
328
|
+
const result: string[] = []
|
|
329
|
+
for (const ref of refs) {
|
|
330
|
+
if (typeof ref !== "string") continue
|
|
331
|
+
const normalized = ref.trim()
|
|
332
|
+
if (!normalized || seen.has(normalized)) continue
|
|
333
|
+
seen.add(normalized)
|
|
334
|
+
result.push(normalized)
|
|
335
|
+
}
|
|
336
|
+
return result
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function normalizeModelRefArray(value: unknown): string[] {
|
|
340
|
+
if (!Array.isArray(value)) return []
|
|
341
|
+
const seen = new Set<string>()
|
|
342
|
+
const result: string[] = []
|
|
343
|
+
for (const ref of value) {
|
|
344
|
+
if (typeof ref !== "string") continue
|
|
345
|
+
const normalized = ref.trim()
|
|
346
|
+
if (!normalized || seen.has(normalized)) continue
|
|
347
|
+
seen.add(normalized)
|
|
348
|
+
result.push(normalized)
|
|
349
|
+
}
|
|
350
|
+
return result
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function normalizeResolvedModelArrays(config: DcpConfig): DcpConfig {
|
|
354
|
+
const autoCompress = config.compress?.autoCompress
|
|
355
|
+
if (!autoCompress || typeof autoCompress !== "object") return config
|
|
356
|
+
return {
|
|
357
|
+
...config,
|
|
358
|
+
compress: {
|
|
359
|
+
...config.compress,
|
|
360
|
+
autoCompress: {
|
|
361
|
+
...autoCompress,
|
|
362
|
+
summarizerModel: normalizeModelRefArray(autoCompress.summarizerModel),
|
|
363
|
+
summarizerFallbackModels: normalizeModelRefArray(autoCompress.summarizerFallbackModels),
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
278
369
|
function escapeRegExp(text: string): string {
|
|
279
370
|
return text.replace(/[|\\{}()[\]^$+?.]/g, "\\$&")
|
|
280
371
|
}
|
|
@@ -378,17 +469,17 @@ export function resolveModelConfig(
|
|
|
378
469
|
modelKeys: Array<string | undefined> = [],
|
|
379
470
|
): DcpConfig {
|
|
380
471
|
const overrides = config.modelOverrides
|
|
381
|
-
if (!overrides || Object.keys(overrides).length === 0) return config
|
|
472
|
+
if (!overrides || Object.keys(overrides).length === 0) return normalizeResolvedModelArrays(config)
|
|
382
473
|
|
|
383
474
|
const matches = matchingModelEntries(overrides, modelKeys)
|
|
384
|
-
if (matches.length === 0) return config
|
|
475
|
+
if (matches.length === 0) return normalizeResolvedModelArrays(config)
|
|
385
476
|
|
|
386
477
|
let resolved = deepMerge(config, {})
|
|
387
478
|
for (const [, override] of matches) {
|
|
388
479
|
resolved = deepMerge(resolved, override as Partial<DcpConfig>)
|
|
389
480
|
}
|
|
390
481
|
|
|
391
|
-
return resolved
|
|
482
|
+
return normalizeResolvedModelArrays(resolved)
|
|
392
483
|
}
|
|
393
484
|
|
|
394
485
|
// ---------------------------------------------------------------------------
|
|
@@ -407,9 +498,15 @@ export interface LoadConfigOptions {
|
|
|
407
498
|
export function loadConfig(options: LoadConfigOptions = {}): DcpConfig {
|
|
408
499
|
// Layer 1: defaults (deep clone so we never mutate the constant)
|
|
409
500
|
let config: DcpConfig = structuredClone(DEFAULT_CONFIG)
|
|
501
|
+
const issues: string[] = []
|
|
410
502
|
|
|
411
503
|
const homeDir = options.homeDir ?? os.homedir()
|
|
412
|
-
config = mergeSuiteDcpConfig(
|
|
504
|
+
config = mergeSuiteDcpConfig(
|
|
505
|
+
config,
|
|
506
|
+
path.join(homeDir, ".config", "pi", "pi-tools-suite.jsonc"),
|
|
507
|
+
issues,
|
|
508
|
+
)
|
|
509
|
+
config.issues = issues
|
|
413
510
|
|
|
414
|
-
return config
|
|
511
|
+
return normalizeResolvedModelArrays(config)
|
|
415
512
|
}
|
|
@@ -76,19 +76,43 @@ function stripDcpCarrierText(text: string): string {
|
|
|
76
76
|
return stripStaleDcpMetadataLines(text)
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
const OMIT_JSON_OBJECT_VALUE = Symbol("omit-json-object-value")
|
|
80
|
+
|
|
81
|
+
function canonicalHashValue(
|
|
82
|
+
value: unknown,
|
|
83
|
+
seen = new WeakSet<object>(),
|
|
84
|
+
arrayElement = false,
|
|
85
|
+
): unknown | typeof OMIT_JSON_OBJECT_VALUE {
|
|
80
86
|
if (value === null || typeof value === "string" || typeof value === "boolean") return value
|
|
81
|
-
|
|
87
|
+
// Exact membership is persisted in JSONL, so hash the value that can actually
|
|
88
|
+
// survive that persistence boundary. JSON.stringify serializes non-finite
|
|
89
|
+
// numbers as null, drops undefined/function/symbol object properties, and
|
|
90
|
+
// converts those values to null when they occupy an array slot. Encoding an
|
|
91
|
+
// in-memory-only sentinel here made otherwise unchanged toolResult.details
|
|
92
|
+
// fail exact replay after restart.
|
|
93
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null
|
|
82
94
|
if (typeof value === "bigint") return `${value}n`
|
|
83
|
-
if (typeof value === "undefined"
|
|
84
|
-
|
|
85
|
-
|
|
95
|
+
if (typeof value === "undefined" || typeof value === "function" || typeof value === "symbol") {
|
|
96
|
+
return arrayElement ? null : OMIT_JSON_OBJECT_VALUE
|
|
97
|
+
}
|
|
98
|
+
if (Array.isArray(value)) {
|
|
99
|
+
return value.map((item) => {
|
|
100
|
+
const canonical = canonicalHashValue(item, seen, true)
|
|
101
|
+
return canonical === OMIT_JSON_OBJECT_VALUE ? null : canonical
|
|
102
|
+
})
|
|
103
|
+
}
|
|
86
104
|
if (typeof value === "object") {
|
|
87
105
|
if (seen.has(value as object)) return "[cycle]"
|
|
106
|
+
const toJSON = (value as { toJSON?: unknown }).toJSON
|
|
107
|
+
if (typeof toJSON === "function") {
|
|
108
|
+
const serialized = (toJSON as () => unknown).call(value)
|
|
109
|
+
return canonicalHashValue(serialized, seen, arrayElement)
|
|
110
|
+
}
|
|
88
111
|
seen.add(value as object)
|
|
89
112
|
const output: Record<string, unknown> = {}
|
|
90
113
|
for (const key of Object.keys(value as Record<string, unknown>).sort()) {
|
|
91
|
-
|
|
114
|
+
const canonical = canonicalHashValue((value as Record<string, unknown>)[key], seen, false)
|
|
115
|
+
if (canonical !== OMIT_JSON_OBJECT_VALUE) output[key] = canonical
|
|
92
116
|
}
|
|
93
117
|
seen.delete(value as object)
|
|
94
118
|
return output
|
|
@@ -106,14 +130,17 @@ export function canonicalMessageHash(message: any): string {
|
|
|
106
130
|
if (message && typeof message === "object") {
|
|
107
131
|
for (const key of Object.keys(message).sort()) {
|
|
108
132
|
if (IDENTITY_ONLY_MESSAGE_KEYS.has(key)) continue
|
|
109
|
-
|
|
133
|
+
const value = key === "content"
|
|
110
134
|
? canonicalContentForMessageHash(message)
|
|
111
135
|
: canonicalHashValue(message[key])
|
|
136
|
+
if (value !== OMIT_JSON_OBJECT_VALUE) canonical[key] = value
|
|
112
137
|
}
|
|
113
138
|
} else {
|
|
114
|
-
|
|
139
|
+
const value = canonicalHashValue(message)
|
|
140
|
+
if (value !== OMIT_JSON_OBJECT_VALUE) canonical.value = value
|
|
115
141
|
}
|
|
116
|
-
|
|
142
|
+
const normalized = canonicalHashValue(canonical)
|
|
143
|
+
return createHash("sha256").update(JSON.stringify(normalized === OMIT_JSON_OBJECT_VALUE ? {} : normalized)).digest("hex")
|
|
117
144
|
}
|
|
118
145
|
|
|
119
146
|
const CANONICAL_HASH_RE = /^[a-f0-9]{64}$/i
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { DcpConfig } from "./config.js";
|
|
2
|
+
import { modelKeysFromContext } from "./config.js";
|
|
3
|
+
import type { DcpState } from "./state.js";
|
|
4
|
+
import { estimateMessageTokens, getActiveSummaryTokenEstimate, resolveContextThresholds } from "./pruner-metadata.js";
|
|
5
|
+
import { planDcpBudget } from "./progress-controller.js";
|
|
6
|
+
|
|
7
|
+
/** Scalar-only diagnostic snapshot; never stores raw messages or credentials.
|
|
8
|
+
* These records are observational, never journal/recovery authority. */
|
|
9
|
+
export function dcpRequestSnapshot(config: DcpConfig, state: DcpState, model: any,
|
|
10
|
+
usage: { tokens?: number | null; contextWindow?: number } | undefined,
|
|
11
|
+
raw: any[], projected: any[], reason: string) {
|
|
12
|
+
const contextWindow = Number.isFinite(model?.contextWindow) && model.contextWindow > 0
|
|
13
|
+
? model.contextWindow : usage?.contextWindow;
|
|
14
|
+
const rawTokens = raw.reduce((sum, message) => sum + estimateMessageTokens(message), 0);
|
|
15
|
+
const projectedTokens = projected.reduce((sum, message) => sum + estimateMessageTokens(message), 0);
|
|
16
|
+
const thresholds = resolveContextThresholds(config, modelKeysFromContext({ model } as any), contextWindow);
|
|
17
|
+
const budget = contextWindow ? planDcpBudget({
|
|
18
|
+
providerUsageTokens: usage?.tokens, repoProjectedTokens: projectedTokens, contextWindow,
|
|
19
|
+
reservedOutputTokens: model?.maxTokens ?? 0, reservedToolTokens: 0,
|
|
20
|
+
maxContextPercent: thresholds.maxContextPercent,
|
|
21
|
+
hardContextPercent: config.strategies.emergencyCurrentTurnPruning.hardContextPercent,
|
|
22
|
+
targetContextPercent: Math.min(config.strategies.emergencyCurrentTurnPruning.targetContextPercent, thresholds.maxContextPercent * 0.9),
|
|
23
|
+
summaryBufferEnabled: config.compress.summaryBuffer,
|
|
24
|
+
activeSummaryTokens: getActiveSummaryTokenEstimate(state), summaryBufferMaxBonusRatio: 0.05,
|
|
25
|
+
estimatorMarginTokens: Math.max(256, Math.ceil(contextWindow * 0.0025)),
|
|
26
|
+
}) : undefined;
|
|
27
|
+
const routineTokens = contextWindow ? thresholds.minContextPercent * contextWindow : undefined;
|
|
28
|
+
let pressure = "unknown";
|
|
29
|
+
if (budget && routineTokens !== undefined) {
|
|
30
|
+
pressure = budget.capacityExceeded ? "over capacity" : budget.hardPressure ? "hard" : budget.pressured ? "strong"
|
|
31
|
+
: budget.projectedBeforeTokens > routineTokens ? "routine" : "below routine threshold";
|
|
32
|
+
}
|
|
33
|
+
return { model: model?.provider && model?.id ? `${model.provider}/${model.id}` : undefined,
|
|
34
|
+
contextWindow, rawTokens, projectedTokens, inputCapacityTokens: budget?.inputCapacityTokens,
|
|
35
|
+
reservedOutputTokens: budget?.reservedOutputTokens, routineTokens, strongTokens: budget?.softHeadroomTokens,
|
|
36
|
+
hardTokens: budget?.hardHeadroomTokens, enabled: config.enabled, manualMode: state.manualMode,
|
|
37
|
+
autoEnabled: config.compress.autoCompress.enabled, ignored: state.consecutiveIgnoredNudges,
|
|
38
|
+
routineProjectedTokens: budget?.projectedBeforeTokens ?? projectedTokens,
|
|
39
|
+
routineUsageAdjustmentTokens: 0,
|
|
40
|
+
pressure, reason, createdAt: Date.now() };
|
|
41
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proof that a tool result was produced locally in this epoch and has never
|
|
3
|
+
* belonged to an attempted provider prefix. This is NOT provider-seen evidence:
|
|
4
|
+
* failed/ambiguous sends consume freshness too. Never persist or rehydrate it.
|
|
5
|
+
*/
|
|
6
|
+
export class FreshToolResultTracker {
|
|
7
|
+
private ready = false;
|
|
8
|
+
private readonly pending = new Map<string, string>();
|
|
9
|
+
private readonly fresh = new Set<string>();
|
|
10
|
+
|
|
11
|
+
reset(): void {
|
|
12
|
+
this.ready = false;
|
|
13
|
+
this.pending.clear();
|
|
14
|
+
this.fresh.clear();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Call before every send, not after successful completion. */
|
|
18
|
+
beforeProviderRequest(): void {
|
|
19
|
+
this.reset();
|
|
20
|
+
this.ready = true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
toolCall(id: string, name: string, alreadyKnown: boolean): void {
|
|
24
|
+
// Resumed/in-flight old calls and reused IDs cannot create a freshness grant.
|
|
25
|
+
if (!this.ready || alreadyKnown) return;
|
|
26
|
+
this.pending.set(id, name);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
toolResult(id: string, name: string): void {
|
|
30
|
+
const expectedName = this.pending.get(id);
|
|
31
|
+
this.pending.delete(id);
|
|
32
|
+
if (expectedName === name) this.fresh.add(id);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get eligibleIds(): ReadonlySet<string> {
|
|
36
|
+
return this.fresh;
|
|
37
|
+
}
|
|
38
|
+
}
|