pi-ui-extend 1.0.40 → 1.0.42
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 +10 -2
- package/dist/app/app.d.ts +3 -0
- package/dist/app/app.js +26 -0
- package/dist/app/commands/command-host.d.ts +1 -0
- package/dist/app/commands/command-model-actions.js +6 -1
- package/dist/app/commands/command-registry.js +3 -3
- package/dist/app/commands/command-session-actions.d.ts +0 -1
- package/dist/app/commands/command-session-actions.js +30 -15
- package/dist/app/commands/reload-context-inventory.d.ts +11 -0
- package/dist/app/commands/reload-context-inventory.js +59 -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/icons.d.ts +14 -0
- package/dist/app/icons.js +33 -0
- package/dist/app/rendering/conversation-tool-renderer.js +2 -2
- package/dist/app/rendering/dcp-stats.d.ts +6 -1
- package/dist/app/rendering/dcp-stats.js +214 -46
- package/dist/app/rendering/editor-panels.js +17 -8
- package/dist/app/session/lazy-session-manager.js +12 -1
- package/dist/app/session/session-lifecycle-controller.d.ts +1 -0
- package/dist/app/session/session-lifecycle-controller.js +6 -0
- package/dist/app/session/tabs-controller.d.ts +3 -5
- package/dist/app/session/tabs-controller.js +31 -22
- package/dist/app/subagents/subagents-files.js +60 -1
- package/dist/app/subagents/subagents-model.d.ts +15 -1
- package/dist/app/subagents/subagents-model.js +52 -17
- package/dist/app/types.d.ts +7 -0
- package/dist/bundled-extensions/session-title/config.js +1 -1
- package/dist/default-pix-config.js +10 -0
- package/dist/markdown-format.js +27 -9
- package/dist/schemas/index.d.ts +1 -0
- package/dist/schemas/index.js +1 -0
- package/dist/schemas/pi-tools-suite-schema.d.ts +106 -71
- package/dist/schemas/pi-tools-suite-schema.js +68 -88
- package/dist/schemas/pix-schema.d.ts +7 -0
- package/dist/schemas/pix-schema.js +14 -0
- package/dist/schemas/tasks-schema.d.ts +18 -0
- package/dist/schemas/tasks-schema.js +41 -0
- package/docs/concurrency.md +6 -0
- package/docs/desktop-mvp.md +12 -1
- package/docs/desktop-task-manager.md +93 -82
- package/external/pi-tools-suite/README.md +192 -72
- package/external/pi-tools-suite/docs/browser-qa-subagent.md +50 -21
- package/external/pi-tools-suite/docs/context-gateway-p00-adr.md +216 -0
- package/external/pi-tools-suite/docs/context-gateway-p01n-gate-review.md +122 -0
- package/external/pi-tools-suite/docs/context-gateway-p01n-measurement.md +133 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-ra-evidence.md +111 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rb-evidence.md +100 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rc-evidence.md +69 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rd-evidence.md +100 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-re-evidence.md +74 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rf-evidence.md +153 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rg-evidence.md +235 -0
- package/external/pi-tools-suite/docs/evals.md +1 -1
- package/external/pi-tools-suite/docs/session-recovery.md +40 -14
- package/external/pi-tools-suite/docs/subagent-model-pools.md +120 -0
- package/external/pi-tools-suite/package.json +3 -0
- package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/scripts/browser-qa-runner.mjs +82 -1
- package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/SKILL.md → agents/browser-qa.md} +261 -12
- 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 +20 -0
- package/external/pi-tools-suite/src/async-subagents/agents/oracle.md +16 -0
- package/external/pi-tools-suite/src/async-subagents/agents/presets.jsonc +16 -0
- package/external/pi-tools-suite/src/async-subagents/agents/research.md +20 -0
- package/external/pi-tools-suite/src/async-subagents/agents/verify.md +18 -0
- package/external/pi-tools-suite/src/async-subagents/commands.ts +20 -72
- 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 +42 -0
- package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +13 -93
- package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +499 -0
- package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +9 -0
- package/external/pi-tools-suite/src/async-subagents/core/config.ts +284 -417
- package/external/pi-tools-suite/src/async-subagents/core/model-fallback.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +54 -0
- package/external/pi-tools-suite/src/async-subagents/core/prompt.ts +7 -6
- package/external/pi-tools-suite/src/async-subagents/core/routing.ts +65 -45
- package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +18 -6
- 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/index.ts +77 -3
- package/external/pi-tools-suite/src/async-subagents/lib.ts +12 -9
- package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +54 -22
- package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +6 -4
- package/external/pi-tools-suite/src/async-subagents/types.ts +3 -0
- package/external/pi-tools-suite/src/config.ts +42 -2
- package/external/pi-tools-suite/src/context-gateway/accounting.ts +151 -0
- package/external/pi-tools-suite/src/context-gateway/config.ts +120 -0
- package/external/pi-tools-suite/src/context-gateway/enforcement.ts +102 -0
- package/external/pi-tools-suite/src/context-gateway/index.ts +214 -0
- package/external/pi-tools-suite/src/context-gateway/metadata-normalization.ts +88 -0
- package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +89 -0
- package/external/pi-tools-suite/src/context-gateway/telemetry.ts +502 -0
- package/external/pi-tools-suite/src/context-gateway/test-output-parser.ts +326 -0
- package/external/pi-tools-suite/src/context-gateway/types.ts +158 -0
- package/external/pi-tools-suite/src/context-inventory.ts +99 -0
- package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +106 -0
- package/external/pi-tools-suite/src/dcp/auto-compress.ts +892 -136
- package/external/pi-tools-suite/src/dcp/commands.ts +82 -139
- package/external/pi-tools-suite/src/dcp/compress-tool.ts +533 -74
- package/external/pi-tools-suite/src/dcp/compression-blocks.ts +488 -94
- package/external/pi-tools-suite/src/dcp/compression-preview.ts +122 -0
- package/external/pi-tools-suite/src/dcp/compression-progress.ts +70 -0
- package/external/pi-tools-suite/src/dcp/config.ts +86 -68
- package/external/pi-tools-suite/src/dcp/conversation-index.ts +421 -0
- package/external/pi-tools-suite/src/dcp/debug-log.ts +7 -5
- package/external/pi-tools-suite/src/dcp/index.ts +617 -203
- package/external/pi-tools-suite/src/dcp/journal.ts +566 -0
- package/external/pi-tools-suite/src/dcp/progress-controller.ts +244 -0
- package/external/pi-tools-suite/src/dcp/prompts.ts +83 -189
- package/external/pi-tools-suite/src/dcp/protected-continuity.ts +175 -0
- package/external/pi-tools-suite/src/dcp/provider-tool-results.ts +189 -0
- package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +299 -79
- package/external/pi-tools-suite/src/dcp/pruner-compression-blocks.ts +173 -281
- package/external/pi-tools-suite/src/dcp/pruner-emergency.ts +2 -4
- package/external/pi-tools-suite/src/dcp/pruner-message-ids.ts +23 -15
- package/external/pi-tools-suite/src/dcp/pruner-metadata.ts +11 -1
- package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +30 -82
- package/external/pi-tools-suite/src/dcp/pruner-tools.ts +28 -136
- package/external/pi-tools-suite/src/dcp/pruner.ts +18 -33
- package/external/pi-tools-suite/src/dcp/recovery.ts +129 -0
- package/external/pi-tools-suite/src/dcp/shadow-plan.ts +127 -0
- package/external/pi-tools-suite/src/dcp/state-transaction.ts +102 -0
- package/external/pi-tools-suite/src/dcp/state.ts +158 -580
- package/external/pi-tools-suite/src/dcp/ui.ts +1 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +36 -214
- package/external/pi-tools-suite/src/index.ts +16 -1
- package/external/pi-tools-suite/src/model-tools/index.ts +76 -42
- package/external/pi-tools-suite/src/repo-discovery/index.ts +84 -18
- package/external/pi-tools-suite/src/repo-discovery/native-compact.ts +458 -0
- package/external/pi-tools-suite/src/resource-registry/index.ts +2290 -0
- package/external/pi-tools-suite/src/session-recovery/index.ts +189 -43
- package/external/pi-tools-suite/src/shell-command-policy.ts +219 -0
- package/external/pi-tools-suite/src/tool-descriptions.ts +41 -38
- package/external/pi-tools-suite/src/truncation-metadata-normalizer/index.ts +17 -0
- package/package.json +6 -3
- package/schemas/pi-tools-suite.json +540 -312
- package/schemas/pix.json +24 -0
- package/schemas/tasks.json +132 -0
- package/skills/spec-wiki/SKILL.md +55 -0
- package/skills/spec-wiki/evals/evals.json +132 -0
- package/skills/spec-wiki/evals/files/bootstrap/architecture/rfc-007.md +14 -0
- package/skills/spec-wiki/evals/files/bootstrap/docs/how-to-pay.md +4 -0
- package/skills/spec-wiki/evals/files/bootstrap/docs/overview.md +9 -0
- package/skills/spec-wiki/evals/files/bootstrap/requirements/payment-behavior.md +17 -0
- package/skills/spec-wiki/evals/files/bootstrap/src/payments.ts +12 -0
- package/skills/spec-wiki/evals/files/bootstrap/tests/payments.test.ts +11 -0
- package/skills/spec-wiki/evals/files/focused/src/session.ts +5 -0
- package/skills/spec-wiki/evals/files/focused/tests/session.test.ts +17 -0
- package/skills/spec-wiki/evals/files/maintenance/low-signal/incoming/capture.txt +2 -0
- package/skills/spec-wiki/evals/files/maintenance/low-signal/src/capture.ts +3 -0
- package/skills/spec-wiki/evals/files/maintenance/move/docs/old-job-contract.md +18 -0
- package/skills/spec-wiki/evals/files/maintenance/move/src/job.ts +5 -0
- package/skills/spec-wiki/evals/files/maintenance/new-relation/docs/session-refresh.md +24 -0
- package/skills/spec-wiki/evals/files/maintenance/new-relation/incoming/refresh-worker.ts +7 -0
- package/skills/spec-wiki/evals/files/maintenance/new-relation/src/session.ts +7 -0
- package/skills/spec-wiki/evals/files/maintenance/no-impact/docs/auth-contract.md +17 -0
- package/skills/spec-wiki/evals/files/maintenance/no-impact/incoming/telemetry-format.ts +3 -0
- package/skills/spec-wiki/evals/files/maintenance/no-impact/src/auth.ts +3 -0
- package/skills/spec-wiki/evals/files/query/docs/how-to-run-jobs.md +4 -0
- package/skills/spec-wiki/evals/files/query/docs/spec-overview.md +9 -0
- package/skills/spec-wiki/evals/files/query/features/jobs/cancellation-contract.md +15 -0
- package/skills/spec-wiki/evals/files/query/features/jobs/retry-spec.md +6 -0
- package/skills/spec-wiki/evals/maintenance_regression.py +311 -0
- package/skills/spec-wiki/evals/retrieval-eval.md +37 -0
- package/skills/spec-wiki/evals/retrieval-evals.json +37 -0
- package/skills/spec-wiki/evals/trigger-evals.json +94 -0
- package/skills/spec-wiki/references/bootstrap.md +100 -0
- package/skills/spec-wiki/references/focused-spec.md +117 -0
- package/skills/spec-wiki/references/index-schema.md +330 -0
- package/skills/spec-wiki/references/maintenance.md +134 -0
- package/skills/spec-wiki/references/query.md +74 -0
- package/skills/spec-wiki/scripts/spec_wiki.py +2076 -0
- package/docs/desktop-markdown-media.md +0 -77
- package/external/pi-tools-suite/docs/dcp-emergency-current-turn.md +0 -102
- package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +0 -270
- package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/auth-scaffold-spec.md +0 -78
- package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/qa-design.md +0 -223
- package/external/pi-tools-suite/src/dcp/state-persistence.ts +0 -195
- package/external/pi-tools-suite/src/skill-installer/index.ts +0 -333
- package/skills/spec-lite/SKILL.md +0 -140
- /package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/references → agents/browser-qa/examples}/qa-auth.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/references → agents/browser-qa/examples}/qa-flow.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/vendor/fflate.LICENSE +0 -0
- /package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/vendor/fflate.mjs +0 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { DcpConfig } from "./config.js"
|
|
2
|
+
import { canonicalMessageHash, copyRawMutationHash } from "./conversation-index.js"
|
|
3
|
+
import { cloneDcpTransactionState } from "./state-transaction.js"
|
|
4
|
+
import type { CompressionBlock, ConversationIndexEntry, DcpState } from "./state.js"
|
|
5
|
+
import { stripStaleDcpMetadataFromMessage, estimateMessageTokens } from "./pruner-metadata.js"
|
|
6
|
+
import { injectMessageIds, stableMessageKeys } from "./pruner-message-ids.js"
|
|
7
|
+
import { applyCompressionBlocks } from "./pruner-compression-blocks.js"
|
|
8
|
+
|
|
9
|
+
/** The latest provider projection is runtime-only and intentionally never persisted. */
|
|
10
|
+
const projectionSnapshots = new WeakMap<ConversationIndexEntry[], any[]>()
|
|
11
|
+
|
|
12
|
+
const DCP_PROVENANCE_KEYS = ["_dcpOrigin", "_dcpStableId", "_dcpBlockId"] as const
|
|
13
|
+
|
|
14
|
+
function copyDcpProvenance(source: any, target: any): any {
|
|
15
|
+
if (!source || !target || typeof source !== "object" || typeof target !== "object") return target
|
|
16
|
+
for (const key of DCP_PROVENANCE_KEYS) {
|
|
17
|
+
const descriptor = Object.getOwnPropertyDescriptor(source, key)
|
|
18
|
+
if (descriptor) Object.defineProperty(target, key, descriptor)
|
|
19
|
+
}
|
|
20
|
+
copyRawMutationHash(source, target)
|
|
21
|
+
return target
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Clone message objects while retaining the non-enumerable DCP provenance used by exact spans. */
|
|
25
|
+
export function cloneProjectionMessages(messages: readonly any[]): any[] {
|
|
26
|
+
return messages.map((message) => {
|
|
27
|
+
const clone = message && typeof message === "object" ? { ...message } : message
|
|
28
|
+
if (!clone || typeof clone !== "object") return clone
|
|
29
|
+
if (message.content && typeof message.content === "object") clone.content = structuredClone(message.content)
|
|
30
|
+
return copyDcpProvenance(message, clone)
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Store a detached copy of the exact provider projection that produced this index. */
|
|
35
|
+
export function recordCompressionProjection(
|
|
36
|
+
entries: ConversationIndexEntry[],
|
|
37
|
+
messages: readonly any[],
|
|
38
|
+
): void {
|
|
39
|
+
projectionSnapshots.set(entries, cloneProjectionMessages(messages))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function verifiedSnapshot(state: DcpState): any[] {
|
|
43
|
+
const snapshot = projectionSnapshots.get(state.conversationIndexSnapshot)
|
|
44
|
+
if (!snapshot || snapshot.length !== state.conversationIndexSnapshot.length) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
"Manual compression requires a current verifiable provider projection; refresh the DCP context and retry.",
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
const stableKeys = stableMessageKeys(snapshot)
|
|
50
|
+
for (let index = 0; index < snapshot.length; index++) {
|
|
51
|
+
const entry = state.conversationIndexSnapshot[index]
|
|
52
|
+
if (!entry || entry.stableId !== stableKeys[index] || entry.contentHash !== canonicalMessageHash(snapshot[index])) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
"Manual compression provider projection no longer matches the current DCP snapshot; refresh the context and retry.",
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return snapshot
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Return a detached, verified copy of the exact provider projection used by
|
|
63
|
+
* manual compression. Generated summaries must use this source rather than
|
|
64
|
+
* reconstructing history from timestamps or persisted session entries.
|
|
65
|
+
*/
|
|
66
|
+
export function verifiedManualCompressionMessages(state: DcpState): any[] {
|
|
67
|
+
return cloneProjectionMessages(verifiedSnapshot(state))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function stripProjectionMetadata(message: any): any {
|
|
71
|
+
const stripped = stripStaleDcpMetadataFromMessage(message)
|
|
72
|
+
return copyDcpProvenance(message, stripped)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ManualCompressionProjection {
|
|
76
|
+
projectedBeforeTokens: number
|
|
77
|
+
projectedAfterTokens: number
|
|
78
|
+
netGain: number
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Materialize only an operation's new blocks against the exact current
|
|
83
|
+
* provider projection. Existing blocks are already represented there as
|
|
84
|
+
* synthetic entries, so replaying them would double-count and hide a bad
|
|
85
|
+
* manual summary behind unrelated automatic pruning.
|
|
86
|
+
*/
|
|
87
|
+
export function previewManualCompressionProjection(
|
|
88
|
+
state: DcpState,
|
|
89
|
+
config: DcpConfig,
|
|
90
|
+
newBlocks: CompressionBlock[],
|
|
91
|
+
): ManualCompressionProjection {
|
|
92
|
+
const source = verifiedSnapshot(state)
|
|
93
|
+
const before = cloneProjectionMessages(source)
|
|
94
|
+
const after = cloneProjectionMessages(source).map(stripProjectionMetadata)
|
|
95
|
+
const previewState = cloneDcpTransactionState(state)
|
|
96
|
+
previewState.compressionBlocks = newBlocks.map((block) => structuredClone(block))
|
|
97
|
+
previewState.accountedCompressionBlockIds = new Set()
|
|
98
|
+
previewState.compressionTokenSavings = new Map()
|
|
99
|
+
|
|
100
|
+
applyCompressionBlocks(after, previewState)
|
|
101
|
+
const appliedBlockIds = new Set(
|
|
102
|
+
after.flatMap((message) => Number.isInteger(message?._dcpBlockId) ? [message._dcpBlockId as number] : []),
|
|
103
|
+
)
|
|
104
|
+
const missing = newBlocks.filter((block) => !appliedBlockIds.has(block.id))
|
|
105
|
+
if (missing.length > 0) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`Manual compression could not materialize ${missing.map((block) => `b${block.id}`).join(", ")} in the current provider projection; no state was published.`,
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Recreate carrier overhead on a detached state. This deliberately happens
|
|
112
|
+
// after applying the wrapper so the comparison includes the full delivered
|
|
113
|
+
// provider projection rather than just summary text.
|
|
114
|
+
injectMessageIds(after, previewState, { config })
|
|
115
|
+
const projectedBeforeTokens = before.reduce((sum, message) => sum + estimateMessageTokens(message), 0)
|
|
116
|
+
const projectedAfterTokens = after.reduce((sum, message) => sum + estimateMessageTokens(message), 0)
|
|
117
|
+
return {
|
|
118
|
+
projectedBeforeTokens,
|
|
119
|
+
projectedAfterTokens,
|
|
120
|
+
netGain: projectedBeforeTokens - projectedAfterTokens,
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { DcpState } from "./state.js"
|
|
2
|
+
|
|
3
|
+
/** Routine recovery aims for up to five percentage points, never below min. */
|
|
4
|
+
export function routineRecoveryTokens(tokens: number, window: number, minPercent: number): number {
|
|
5
|
+
return Math.max(1, Math.ceil(Math.min(window * 0.05, Math.max(0, tokens - window * minPercent))))
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface CompressionProgressInput {
|
|
9
|
+
projectedTokens: number
|
|
10
|
+
contextWindow: number
|
|
11
|
+
requiredTokens: number
|
|
12
|
+
kind: "routine" | "emergency"
|
|
13
|
+
observedTokens?: number
|
|
14
|
+
targetHeadroomTokens?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Remaining debt follows new content/usage, not a stale pre-compression sample. */
|
|
18
|
+
export function outstandingCompressionTokens(state: DcpState, input: CompressionProgressInput): number | undefined {
|
|
19
|
+
const previous = state.compressionProgress
|
|
20
|
+
if (!previous || previous.contextWindow !== input.contextWindow) return undefined
|
|
21
|
+
const projectionChange = input.projectedTokens - previous.projectedTokens
|
|
22
|
+
const observedGrowth = input.observedTokens !== undefined && previous.observedTokens !== undefined
|
|
23
|
+
? Math.max(0, input.observedTokens - previous.observedTokens) : 0
|
|
24
|
+
const growth = observedGrowth > 0 ? Math.max(projectionChange, observedGrowth) : projectionChange
|
|
25
|
+
const stricterTarget = previous.kind === input.kind && previous.targetHeadroomTokens !== undefined && input.targetHeadroomTokens !== undefined
|
|
26
|
+
? Math.max(0, previous.targetHeadroomTokens - input.targetHeadroomTokens) : 0
|
|
27
|
+
return Math.max(0, previous.remainingTokens + growth + stricterTarget)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Follow actual projection growth, not context callback/retry count. */
|
|
31
|
+
export function trackCompressionProgress(
|
|
32
|
+
state: DcpState,
|
|
33
|
+
input: CompressionProgressInput,
|
|
34
|
+
): void {
|
|
35
|
+
const previous = state.compressionProgress
|
|
36
|
+
const remaining = outstandingCompressionTokens(state, input)
|
|
37
|
+
state.compressionProgress = {
|
|
38
|
+
projectedTokens: input.projectedTokens,
|
|
39
|
+
contextWindow: input.contextWindow,
|
|
40
|
+
// The unchanged native sample still describes the input before a manual
|
|
41
|
+
// commit. Do not charge the same already-recovered tokens on every callback.
|
|
42
|
+
remainingTokens: previous && previous.kind === input.kind && remaining !== undefined && remaining > 0
|
|
43
|
+
? remaining : Math.max(1, input.requiredTokens, remaining ?? 0),
|
|
44
|
+
kind: input.kind,
|
|
45
|
+
observedTokens: input.observedTokens,
|
|
46
|
+
targetHeadroomTokens: previous?.kind === input.kind && previous.targetHeadroomTokens !== undefined && input.targetHeadroomTokens !== undefined
|
|
47
|
+
? Math.min(previous.targetHeadroomTokens, input.targetHeadroomTokens) : input.targetHeadroomTokens,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function resetCompressionProgress(state: DcpState): void {
|
|
52
|
+
state.consecutiveIgnoredStrongNudges = 0
|
|
53
|
+
state.consecutiveIgnoredNudges = 0
|
|
54
|
+
state.compressionProgress = undefined
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Called on a detached state only after positive full-projection gain is proved. */
|
|
58
|
+
export function settleCompressionProgress(state: DcpState, gain: number, projectedAfterTokens: number): boolean {
|
|
59
|
+
const goal = state.compressionProgress
|
|
60
|
+
if (!goal || gain >= goal.remainingTokens) {
|
|
61
|
+
resetCompressionProgress(state)
|
|
62
|
+
return true
|
|
63
|
+
}
|
|
64
|
+
state.compressionProgress = {
|
|
65
|
+
...goal,
|
|
66
|
+
remainingTokens: goal.remainingTokens - gain,
|
|
67
|
+
projectedTokens: projectedAfterTokens,
|
|
68
|
+
}
|
|
69
|
+
return false
|
|
70
|
+
}
|
|
@@ -10,13 +10,14 @@ 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
|
|
16
18
|
}
|
|
17
19
|
manualMode: {
|
|
18
20
|
enabled: boolean
|
|
19
|
-
automaticStrategies: boolean // run dedup/purge even in manual mode
|
|
20
21
|
}
|
|
21
22
|
compress: {
|
|
22
23
|
maxContextPercent: number | string // accepts a 0-1 fraction, absolute tokens, or a percent string like "80%"
|
|
@@ -50,41 +51,27 @@ export interface DcpConfig {
|
|
|
50
51
|
maxSuggestions: number
|
|
51
52
|
}
|
|
52
53
|
/**
|
|
53
|
-
* Auto-compress fallback:
|
|
54
|
-
*
|
|
55
|
-
*
|
|
54
|
+
* Auto-compress fallback: after completed actionable provider opportunities
|
|
55
|
+
* fail to meet the recovery goal, DCP can create a safe compression block,
|
|
56
|
+
* including after routine-reminder escalation. Lossy within a
|
|
56
57
|
* session — disabled by default; opt in via config.
|
|
57
58
|
*/
|
|
58
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. */
|
|
59
62
|
enabled: boolean
|
|
60
|
-
/**
|
|
61
|
-
*
|
|
63
|
+
/** Auto fallback is eligible after more than this many completed,
|
|
64
|
+
* correlated opportunities with a delivered actionable reminder.
|
|
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.
|
|
64
68
|
* Empty array → deterministic programmatic digest (no model call). */
|
|
65
69
|
summarizerModel: string[]
|
|
66
|
-
/** Hard ceiling in ms for a
|
|
70
|
+
/** Hard ceiling in ms for a summarizer model call on either path. */
|
|
67
71
|
timeoutMs: number
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
74
|
strategies: {
|
|
71
|
-
deduplication: {
|
|
72
|
-
enabled: boolean
|
|
73
|
-
protectedTools: string[]
|
|
74
|
-
}
|
|
75
|
-
purgeErrors: {
|
|
76
|
-
enabled: boolean
|
|
77
|
-
turns: number // prune error inputs after N user turns (default: 4)
|
|
78
|
-
protectedTools: string[]
|
|
79
|
-
}
|
|
80
|
-
autoToolPruning: {
|
|
81
|
-
enabled: boolean
|
|
82
|
-
maxOutputTokens: number
|
|
83
|
-
keepRecentTurns: number
|
|
84
|
-
readLikeTools: string[]
|
|
85
|
-
readLikeTurns: number
|
|
86
|
-
protectedTools: string[]
|
|
87
|
-
}
|
|
88
75
|
emergencyCurrentTurnPruning: {
|
|
89
76
|
/** Enable same-turn candidates and lossy fallback pruning; emergency reminders remain active. */
|
|
90
77
|
enabled: boolean
|
|
@@ -104,11 +91,10 @@ export interface DcpConfig {
|
|
|
104
91
|
}
|
|
105
92
|
}
|
|
106
93
|
protectedFilePatterns: string[]
|
|
107
|
-
pruneNotification: "off" | "minimal" | "detailed"
|
|
108
94
|
modelOverrides: Record<string, DcpConfigOverride>
|
|
109
95
|
}
|
|
110
96
|
|
|
111
|
-
export type DcpConfigOverride = DeepPartial<Omit<DcpConfig, "modelOverrides">>
|
|
97
|
+
export type DcpConfigOverride = DeepPartial<Omit<DcpConfig, "modelOverrides" | "issues">>
|
|
112
98
|
|
|
113
99
|
type DeepPartial<T> = T extends Array<infer U>
|
|
114
100
|
? Array<DeepPartial<U>>
|
|
@@ -123,9 +109,9 @@ type DeepPartial<T> = T extends Array<infer U>
|
|
|
123
109
|
const DEFAULT_CONFIG: DcpConfig = {
|
|
124
110
|
enabled: true,
|
|
125
111
|
debug: false,
|
|
112
|
+
issues: [],
|
|
126
113
|
manualMode: {
|
|
127
114
|
enabled: false,
|
|
128
|
-
automaticStrategies: true,
|
|
129
115
|
},
|
|
130
116
|
compress: {
|
|
131
117
|
maxContextPercent: 0.65,
|
|
@@ -162,39 +148,6 @@ const DEFAULT_CONFIG: DcpConfig = {
|
|
|
162
148
|
},
|
|
163
149
|
},
|
|
164
150
|
strategies: {
|
|
165
|
-
deduplication: {
|
|
166
|
-
enabled: true,
|
|
167
|
-
protectedTools: [],
|
|
168
|
-
},
|
|
169
|
-
purgeErrors: {
|
|
170
|
-
enabled: true,
|
|
171
|
-
turns: 4,
|
|
172
|
-
protectedTools: [],
|
|
173
|
-
},
|
|
174
|
-
autoToolPruning: {
|
|
175
|
-
enabled: true,
|
|
176
|
-
maxOutputTokens: 1200,
|
|
177
|
-
keepRecentTurns: 1,
|
|
178
|
-
readLikeTools: [
|
|
179
|
-
"read",
|
|
180
|
-
"shell",
|
|
181
|
-
"bash",
|
|
182
|
-
"grep",
|
|
183
|
-
"find",
|
|
184
|
-
"ls",
|
|
185
|
-
"web_search",
|
|
186
|
-
"web_fetch",
|
|
187
|
-
"repo_architecture",
|
|
188
|
-
"repo_structure",
|
|
189
|
-
"repo_ast",
|
|
190
|
-
"repo_search",
|
|
191
|
-
"repo_explain",
|
|
192
|
-
"repo_deps",
|
|
193
|
-
"ast_grep",
|
|
194
|
-
],
|
|
195
|
-
readLikeTurns: 3,
|
|
196
|
-
protectedTools: [],
|
|
197
|
-
},
|
|
198
151
|
emergencyCurrentTurnPruning: {
|
|
199
152
|
enabled: true,
|
|
200
153
|
hardContextPercent: 0.82,
|
|
@@ -207,7 +160,6 @@ const DEFAULT_CONFIG: DcpConfig = {
|
|
|
207
160
|
},
|
|
208
161
|
},
|
|
209
162
|
protectedFilePatterns: [],
|
|
210
|
-
pruneNotification: "detailed",
|
|
211
163
|
modelOverrides: {},
|
|
212
164
|
}
|
|
213
165
|
|
|
@@ -281,15 +233,75 @@ function readJsoncFile(filePath: string): Record<string, unknown> {
|
|
|
281
233
|
/**
|
|
282
234
|
* Return the nested DCP config from a shared pi-tools-suite config file.
|
|
283
235
|
*/
|
|
284
|
-
function readDcpFromSuiteConfig(filePath: string): Record<string, unknown> {
|
|
236
|
+
function readDcpFromSuiteConfig(filePath: string, issues: string[]): Record<string, unknown> {
|
|
285
237
|
const raw = readJsoncFile(filePath)
|
|
286
238
|
const dcp = raw["dcp"]
|
|
287
239
|
if (dcp === null || typeof dcp !== "object" || Array.isArray(dcp)) return {}
|
|
288
|
-
return dcp as Record<string, unknown
|
|
240
|
+
return stripRemovedDcpKeys(dcp as Record<string, unknown>, issues)
|
|
289
241
|
}
|
|
290
242
|
|
|
291
|
-
|
|
292
|
-
|
|
243
|
+
const REMOVED_DCP_GUIDANCE: Record<string, string> = {
|
|
244
|
+
pruneNotification: "removed; pruning notifications are no longer configurable",
|
|
245
|
+
"manualMode.automaticStrategies": "removed; manual mode now has one explicit enabled flag",
|
|
246
|
+
"strategies.deduplication": "removed; no automatic replacement policy is currently mapped to this setting",
|
|
247
|
+
"strategies.purgeErrors": "removed; no automatic replacement policy is currently mapped to this setting",
|
|
248
|
+
"strategies.autoToolPruning": "removed; use explicit /dcp sweep or DCP compression/emergency policies instead",
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function reportRemovedKey(issues: string[], prefix: string, key: string): void {
|
|
252
|
+
const fullKey = prefix ? `${prefix}.${key}` : key
|
|
253
|
+
const canonical = fullKey.replace(/^modelOverrides\.[^.]+\./, "")
|
|
254
|
+
const guidance = REMOVED_DCP_GUIDANCE[canonical] ?? "removed and ignored"
|
|
255
|
+
issues.push(`dcp.${fullKey} is ${guidance}.`)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function stripRemovedDcpKeys(
|
|
259
|
+
raw: Record<string, unknown>,
|
|
260
|
+
issues: string[],
|
|
261
|
+
prefix = "",
|
|
262
|
+
): Record<string, unknown> {
|
|
263
|
+
const cleaned = structuredClone(raw)
|
|
264
|
+
if (Object.prototype.hasOwnProperty.call(cleaned, "pruneNotification")) {
|
|
265
|
+
reportRemovedKey(issues, prefix, "pruneNotification")
|
|
266
|
+
}
|
|
267
|
+
delete cleaned.pruneNotification
|
|
268
|
+
|
|
269
|
+
const manualMode = cleaned.manualMode
|
|
270
|
+
if (manualMode && typeof manualMode === "object" && !Array.isArray(manualMode)) {
|
|
271
|
+
if (Object.prototype.hasOwnProperty.call(manualMode, "automaticStrategies")) {
|
|
272
|
+
reportRemovedKey(issues, prefix, "manualMode.automaticStrategies")
|
|
273
|
+
}
|
|
274
|
+
delete (manualMode as Record<string, unknown>).automaticStrategies
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const strategies = cleaned.strategies
|
|
278
|
+
if (strategies && typeof strategies === "object" && !Array.isArray(strategies)) {
|
|
279
|
+
const record = strategies as Record<string, unknown>
|
|
280
|
+
for (const key of ["deduplication", "purgeErrors", "autoToolPruning"] as const) {
|
|
281
|
+
if (Object.prototype.hasOwnProperty.call(record, key)) {
|
|
282
|
+
reportRemovedKey(issues, prefix, `strategies.${key}`)
|
|
283
|
+
}
|
|
284
|
+
delete record[key]
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const modelOverrides = cleaned.modelOverrides
|
|
289
|
+
if (modelOverrides && typeof modelOverrides === "object" && !Array.isArray(modelOverrides)) {
|
|
290
|
+
for (const [key, override] of Object.entries(modelOverrides as Record<string, unknown>)) {
|
|
291
|
+
if (!override || typeof override !== "object" || Array.isArray(override)) continue
|
|
292
|
+
;(modelOverrides as Record<string, unknown>)[key] = stripRemovedDcpKeys(
|
|
293
|
+
override as Record<string, unknown>,
|
|
294
|
+
issues,
|
|
295
|
+
prefix ? `${prefix}.modelOverrides.${key}` : `modelOverrides.${key}`,
|
|
296
|
+
)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return cleaned
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function mergeSuiteDcpConfig(config: DcpConfig, filePath: string, issues: string[]): DcpConfig {
|
|
304
|
+
const raw = readDcpFromSuiteConfig(filePath, issues)
|
|
293
305
|
if (Object.keys(raw).length === 0) return config
|
|
294
306
|
return deepMerge(config, raw as Partial<DcpConfig>)
|
|
295
307
|
}
|
|
@@ -431,10 +443,16 @@ export interface LoadConfigOptions {
|
|
|
431
443
|
*/
|
|
432
444
|
export function loadConfig(options: LoadConfigOptions = {}): DcpConfig {
|
|
433
445
|
// Layer 1: defaults (deep clone so we never mutate the constant)
|
|
434
|
-
let config: DcpConfig =
|
|
446
|
+
let config: DcpConfig = structuredClone(DEFAULT_CONFIG)
|
|
447
|
+
const issues: string[] = []
|
|
435
448
|
|
|
436
449
|
const homeDir = options.homeDir ?? os.homedir()
|
|
437
|
-
config = mergeSuiteDcpConfig(
|
|
450
|
+
config = mergeSuiteDcpConfig(
|
|
451
|
+
config,
|
|
452
|
+
path.join(homeDir, ".config", "pi", "pi-tools-suite.jsonc"),
|
|
453
|
+
issues,
|
|
454
|
+
)
|
|
455
|
+
config.issues = issues
|
|
438
456
|
|
|
439
457
|
return config
|
|
440
458
|
}
|