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
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
import {
|
|
32
32
|
applyPruning,
|
|
33
33
|
getNudgeType,
|
|
34
|
+
detectCompressionBlockConsolidationCandidate,
|
|
34
35
|
detectCompressionCandidate,
|
|
35
36
|
detectEmergencyCompressionCandidate,
|
|
36
37
|
detectMessageCompressionCandidates,
|
|
@@ -60,6 +61,11 @@ import {
|
|
|
60
61
|
import { DCP_STATS_MESSAGE_TYPE, registerCommands } from "./commands.js"
|
|
61
62
|
import { normalizeDcpContextUsage } from "./ui.js"
|
|
62
63
|
import { safeGetContextUsage } from "../context-usage.js"
|
|
64
|
+
import { FreshToolResultTracker } from "./fresh-tool-results.js"
|
|
65
|
+
import { RoutinePressureTracker } from "./routine-pressure.js"
|
|
66
|
+
import { canonicalMessageHash } from "./conversation-index.js"
|
|
67
|
+
import { randomUUID } from "node:crypto"
|
|
68
|
+
import { dcpRequestSnapshot } from "./diagnostics.js"
|
|
63
69
|
import {
|
|
64
70
|
collectProviderToolResultEvidence,
|
|
65
71
|
providerPayloadIncludesToolResult,
|
|
@@ -68,23 +74,18 @@ import {
|
|
|
68
74
|
ProviderEvidenceTracker,
|
|
69
75
|
} from "./provider-tool-results.js"
|
|
70
76
|
import { rehydrateToolRecordsFromMessages } from "./recovery.js"
|
|
71
|
-
import { inferDcpBlockedReason, planDcpBudget } from "./progress-controller.js"
|
|
77
|
+
import { inferDcpBlockedReason, planDcpBudget, type DcpBlockedReason } from "./progress-controller.js"
|
|
72
78
|
import { createBudgetedAutoCompressionBlock } from "./auto-compress-budget.js"
|
|
73
|
-
import { outstandingCompressionTokens, resetCompressionProgress, routineRecoveryTokens, trackCompressionProgress } from "./compression-progress.js"
|
|
79
|
+
import { compressionPlanningTokens, outstandingCompressionTokens, resetCompressionProgress, routineRecoveryTokens, trackCompressionProgress } from "./compression-progress.js"
|
|
74
80
|
import { captureDcpTransactionGuard, cloneDcpTransactionState, runDcpStateTransaction, invalidateDcpStateOwner } from "./state-transaction.js"
|
|
75
81
|
|
|
82
|
+
const PIX_DCP_RUNTIME_STATS_SYMBOL = Symbol.for("pix.dcp.runtime-stats")
|
|
83
|
+
|
|
76
84
|
// ---------------------------------------------------------------------------
|
|
77
85
|
// Helpers
|
|
78
86
|
// ---------------------------------------------------------------------------
|
|
79
87
|
|
|
80
|
-
function annotateMessagesWithBranchEntryIds(messages: any[],
|
|
81
|
-
let branch: any[] = []
|
|
82
|
-
try {
|
|
83
|
-
branch = ctx.sessionManager.getBranch()
|
|
84
|
-
} catch {
|
|
85
|
-
return
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
+
function annotateMessagesWithBranchEntryIds(messages: any[], branch: any[]): void {
|
|
88
89
|
const entries = branch.filter((entry) => entry?.type === "message" && entry.message)
|
|
89
90
|
let searchFrom = 0
|
|
90
91
|
for (const msg of messages) {
|
|
@@ -97,6 +98,7 @@ function annotateMessagesWithBranchEntryIds(messages: any[], ctx: ExtensionConte
|
|
|
97
98
|
Number.isFinite(msg?.timestamp) &&
|
|
98
99
|
entryMsg.timestamp !== msg.timestamp
|
|
99
100
|
) continue
|
|
101
|
+
if (canonicalMessageHash(stripStaleDcpMetadataFromMessage(entryMsg)) !== canonicalMessageHash(msg)) continue
|
|
100
102
|
msg._dcpEntryId = entry.id
|
|
101
103
|
searchFrom = i + 1
|
|
102
104
|
break
|
|
@@ -125,6 +127,20 @@ function isDcpControlPlaneMessage(message: any): boolean {
|
|
|
125
127
|
return message?.role === "custom" && DCP_CONTROL_PLANE_CUSTOM_TYPES.has(message.customType)
|
|
126
128
|
}
|
|
127
129
|
|
|
130
|
+
function withoutAutoSummaryModels(config: ReturnType<typeof loadConfig>): ReturnType<typeof loadConfig> {
|
|
131
|
+
return {
|
|
132
|
+
...config,
|
|
133
|
+
compress: {
|
|
134
|
+
...config.compress,
|
|
135
|
+
autoCompress: {
|
|
136
|
+
...config.compress.autoCompress,
|
|
137
|
+
summarizerModel: [],
|
|
138
|
+
summarizerFallbackModels: [],
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
128
144
|
// ---------------------------------------------------------------------------
|
|
129
145
|
// Module export
|
|
130
146
|
// ---------------------------------------------------------------------------
|
|
@@ -132,6 +148,10 @@ function isDcpControlPlaneMessage(message: any): boolean {
|
|
|
132
148
|
export default async function dcpModule(pi: ExtensionAPI, dependencies: { config?: ReturnType<typeof loadConfig>; state?: ReturnType<typeof createState> } = {}): Promise<void> {
|
|
133
149
|
// ── 1. Load config ────────────────────────────────────────────────────────
|
|
134
150
|
const config = dependencies.config ?? loadConfig()
|
|
151
|
+
const runtimeGlobals = globalThis as typeof globalThis & Record<symbol, unknown>
|
|
152
|
+
// Extension reloads may disable DCP. Clear any getter left by the previous
|
|
153
|
+
// module instance before deciding whether this instance is active.
|
|
154
|
+
runtimeGlobals[PIX_DCP_RUNTIME_STATS_SYMBOL] = undefined
|
|
135
155
|
const configForContext = (ctx: unknown) => resolveModelConfig(config, modelKeysFromContext(ctx))
|
|
136
156
|
const hasEnabledModelOverride = Object.values(config.modelOverrides).some(
|
|
137
157
|
(override) => override.enabled === true,
|
|
@@ -141,15 +161,33 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
141
161
|
|
|
142
162
|
// ── 2. Create state ───────────────────────────────────────────────────────
|
|
143
163
|
const state = dependencies.state ?? createState()
|
|
164
|
+
// Pix ACP runs one pi process per session. Expose only this tiny live metric
|
|
165
|
+
// to the patched RPC session-stats surface; DCP state remains extension-owned.
|
|
166
|
+
runtimeGlobals[PIX_DCP_RUNTIME_STATS_SYMBOL] = () => ({ tokensSaved: state.tokensSaved })
|
|
144
167
|
let journalMirror: DcpJournalMirror | undefined
|
|
145
168
|
let journalSupported = false
|
|
146
169
|
let journalBlockedReason: string | undefined
|
|
147
170
|
let journalPersistent = false
|
|
148
171
|
const providerEvidenceTracker = new ProviderEvidenceTracker()
|
|
172
|
+
const freshToolResults = new FreshToolResultTracker()
|
|
173
|
+
const routinePressureTracker = new RoutinePressureTracker()
|
|
149
174
|
let providerEvidenceCommitQueue = Promise.resolve()
|
|
150
175
|
let latestProviderOpportunityAvailable = false
|
|
151
176
|
let latestProviderOpportunityKind: "routine" | "emergency" | undefined
|
|
152
177
|
let latestProviderReminder: string | undefined
|
|
178
|
+
let autoSummarizerDegraded = false
|
|
179
|
+
let lastProviderReadyProjectionEpoch: number | undefined
|
|
180
|
+
let diagnosticEpoch = randomUUID()
|
|
181
|
+
let diagnosticSnapshot: ReturnType<typeof dcpRequestSnapshot> | undefined
|
|
182
|
+
const diagnostic = (event: string, data: Record<string, unknown> = {}) => {
|
|
183
|
+
try { pi.appendEntry("dcp-diagnostic", { version: 1, event, epoch: diagnosticEpoch, ...data }) }
|
|
184
|
+
catch { /* Diagnostics must never affect a transaction or provider send. */ }
|
|
185
|
+
}
|
|
186
|
+
const resetDiagnostics = (reason: string) => {
|
|
187
|
+
diagnosticEpoch = randomUUID()
|
|
188
|
+
diagnosticSnapshot = undefined
|
|
189
|
+
diagnostic("epoch", { reason })
|
|
190
|
+
}
|
|
153
191
|
const warnedProgress = new Set<string>()
|
|
154
192
|
const isJournalSessionSupported = () => journalSupported && journalBlockedReason === undefined
|
|
155
193
|
const persistJournalState = async (
|
|
@@ -174,15 +212,18 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
174
212
|
entry?.type === "message" || entry?.type === "custom_message" || entry?.type === "compaction" || entry?.type === "branch_summary",
|
|
175
213
|
)
|
|
176
214
|
const loadJournalState = async (ctx: ExtensionContext, allowInitialize: boolean): Promise<void> => {
|
|
215
|
+
const epoch = state.sessionEpoch
|
|
177
216
|
journalMirror = undefined
|
|
178
217
|
journalSupported = false
|
|
179
|
-
journalBlockedReason =
|
|
218
|
+
journalBlockedReason = "DCP journal load in progress"
|
|
180
219
|
journalPersistent = false
|
|
181
|
-
const branch = await readDcpJournalBranch(ctx)
|
|
182
220
|
const manager = ctx.sessionManager as any
|
|
183
221
|
const persistentSession = (typeof manager.isPersisted === "function" && manager.isPersisted())
|
|
184
222
|
|| (typeof manager.getSessionFile === "function" && Boolean(manager.getSessionFile()))
|
|
185
223
|
try {
|
|
224
|
+
const branch = await readDcpJournalBranch(ctx)
|
|
225
|
+
if (state.sessionEpoch !== epoch) return
|
|
226
|
+
journalBlockedReason = undefined
|
|
186
227
|
const replay = replayDcpJournal(branch, state)
|
|
187
228
|
if (replay.initialized && replay.lastOperationId) {
|
|
188
229
|
journalSupported = true
|
|
@@ -203,6 +244,7 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
203
244
|
// compatibility checkpoint from existing history.
|
|
204
245
|
journalSupported = false
|
|
205
246
|
} catch (error) {
|
|
247
|
+
if (state.sessionEpoch !== epoch) return
|
|
206
248
|
journalBlockedReason = error instanceof Error ? error.message : String(error)
|
|
207
249
|
journalSupported = false
|
|
208
250
|
journalMirror = undefined
|
|
@@ -242,12 +284,20 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
242
284
|
}
|
|
243
285
|
const invalidateOwner = () => {
|
|
244
286
|
invalidateDcpStateOwner(state)
|
|
287
|
+
routinePressureTracker.reset()
|
|
288
|
+
// Successful exposure belongs to the old provider/branch too, not only
|
|
289
|
+
// the pending attempt. The new owner must establish its own evidence.
|
|
290
|
+
state.providerSeenToolIds.clear()
|
|
291
|
+
freshToolResults.reset()
|
|
245
292
|
providerEvidenceTracker.reset()
|
|
246
293
|
latestProviderOpportunityAvailable = false
|
|
247
294
|
latestProviderOpportunityKind = undefined
|
|
248
295
|
latestProviderReminder = undefined
|
|
296
|
+
autoSummarizerDegraded = false
|
|
297
|
+
lastProviderReadyProjectionEpoch = undefined
|
|
249
298
|
resetCompressionProgress(state)
|
|
250
299
|
warnedProgress.clear()
|
|
300
|
+
resetDiagnostics("owner-changed")
|
|
251
301
|
}
|
|
252
302
|
pi.on("model_select", invalidateOwner)
|
|
253
303
|
pi.on("session_tree", async (_event, ctx) => {
|
|
@@ -304,13 +354,20 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
304
354
|
// ── 5. session_start: restore state from session entries ──────────────────
|
|
305
355
|
pi.on("session_start", async (event, ctx) => {
|
|
306
356
|
resetState(state)
|
|
357
|
+
routinePressureTracker.reset()
|
|
358
|
+
freshToolResults.reset()
|
|
359
|
+
lastProviderReadyProjectionEpoch = undefined
|
|
307
360
|
providerEvidenceTracker.reset()
|
|
308
361
|
latestProviderOpportunityAvailable = false
|
|
309
362
|
latestProviderOpportunityKind = undefined
|
|
310
363
|
latestProviderReminder = undefined
|
|
364
|
+
autoSummarizerDegraded = false
|
|
311
365
|
warnedProgress.clear()
|
|
312
366
|
if (config.manualMode.enabled) state.manualMode = true
|
|
367
|
+
const startingEpoch = state.sessionEpoch
|
|
313
368
|
await loadJournalState(ctx, event.reason === "new" || event.reason === "startup")
|
|
369
|
+
if (state.sessionEpoch !== startingEpoch) return
|
|
370
|
+
resetDiagnostics(event.reason)
|
|
314
371
|
writeDcpDebugLog(configForContext(ctx), "session_start.journal", {
|
|
315
372
|
reason: event.reason,
|
|
316
373
|
supported: journalSupported,
|
|
@@ -330,6 +387,8 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
330
387
|
// Journal operations are committed at the mutation boundary; shutdown does
|
|
331
388
|
// not write a full runtime snapshot.
|
|
332
389
|
pi.on("session_shutdown", async () => {
|
|
390
|
+
routinePressureTracker.reset()
|
|
391
|
+
freshToolResults.reset()
|
|
333
392
|
journalMirror = undefined
|
|
334
393
|
journalSupported = false
|
|
335
394
|
journalBlockedReason = undefined
|
|
@@ -355,6 +414,9 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
355
414
|
|
|
356
415
|
// ── 8. tool_call: record input args for dedup / purge fingerprinting ───────
|
|
357
416
|
pi.on("tool_call", async (event, _ctx) => {
|
|
417
|
+
if (configForContext(_ctx).enabled && isJournalSessionSupported()) {
|
|
418
|
+
freshToolResults.toolCall(event.toolCallId, event.toolName, state.toolCalls.has(event.toolCallId))
|
|
419
|
+
}
|
|
358
420
|
if (!state.toolCalls.has(event.toolCallId)) {
|
|
359
421
|
state.toolCalls.set(event.toolCallId, {
|
|
360
422
|
toolCallId: event.toolCallId,
|
|
@@ -375,6 +437,7 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
375
437
|
|
|
376
438
|
// ── 9. tool_result: finalise tool record with result info ─────────────────
|
|
377
439
|
pi.on("tool_result", async (event, _ctx) => {
|
|
440
|
+
freshToolResults.toolResult(event.toolCallId, event.toolName)
|
|
378
441
|
const record = state.toolCalls.get(event.toolCallId)
|
|
379
442
|
|
|
380
443
|
const outputText = event.content
|
|
@@ -414,7 +477,29 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
414
477
|
const contextMessages = event.messages
|
|
415
478
|
.filter((message: any) => !isUserVisibleOnlyMessage(message) && !isDcpControlPlaneMessage(message))
|
|
416
479
|
.map((message: any) => stripStaleDcpMetadataFromMessage(message))
|
|
417
|
-
const
|
|
480
|
+
const rawContextTokens = contextMessages.reduce((sum, message) => sum + estimateMessageTokens(message), 0)
|
|
481
|
+
const pressureOwner = JSON.stringify([contextEpoch, ...modelKeysFromContext(ctx), ctx.model?.contextWindow, ctx.model?.maxTokens])
|
|
482
|
+
const finishContext = (
|
|
483
|
+
reason: string,
|
|
484
|
+
messages: any[],
|
|
485
|
+
details: Record<string, unknown> = {},
|
|
486
|
+
providerReady = true,
|
|
487
|
+
) => {
|
|
488
|
+
if (state.sessionEpoch !== contextEpoch) throw new DcpJournalError("DCP projection belongs to a stale owner")
|
|
489
|
+
lastProviderReadyProjectionEpoch = providerReady ? state.sessionEpoch : undefined
|
|
490
|
+
const observedUsage = safeGetContextUsage(ctx)
|
|
491
|
+
diagnosticSnapshot = dcpRequestSnapshot(effectiveConfig, state, ctx.model,
|
|
492
|
+
observedUsage, contextMessages, messages, reason)
|
|
493
|
+
const routine = routinePressureTracker.estimate(pressureOwner,
|
|
494
|
+
normalizeDcpContextUsage(observedUsage)?.tokens,
|
|
495
|
+
contextMessages, rawContextTokens, diagnosticSnapshot.projectedTokens, state.compressionBlocks)
|
|
496
|
+
diagnosticSnapshot.routineProjectedTokens = routine.projectedTokens
|
|
497
|
+
diagnosticSnapshot.routineUsageAdjustmentTokens = routine.adjustmentTokens
|
|
498
|
+
if (diagnosticSnapshot.pressure === "routine" && routine.projectedTokens <= (diagnosticSnapshot.routineTokens ?? 0)) {
|
|
499
|
+
diagnosticSnapshot.pressure = "below routine threshold"
|
|
500
|
+
}
|
|
501
|
+
if (providerReady) routinePressureTracker.prepare(pressureOwner, rawContextTokens,
|
|
502
|
+
diagnosticSnapshot.projectedTokens, state.compressionBlocks)
|
|
418
503
|
writeDcpDebugLog(effectiveConfig, "context.result", {
|
|
419
504
|
reason,
|
|
420
505
|
inputMessages: event.messages.length,
|
|
@@ -434,6 +519,7 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
434
519
|
}, ctx)
|
|
435
520
|
latestProviderOpportunityAvailable = false
|
|
436
521
|
if (!effectiveConfig.enabled) {
|
|
522
|
+
routinePressureTracker.reset()
|
|
437
523
|
writeDcpDebugLog(effectiveConfig, "context.disabled", {
|
|
438
524
|
inputMessages: event.messages.length,
|
|
439
525
|
filteredMessages: contextMessages.length,
|
|
@@ -448,7 +534,12 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
448
534
|
}
|
|
449
535
|
latestProviderOpportunityKind = undefined
|
|
450
536
|
latestProviderReminder = undefined
|
|
451
|
-
|
|
537
|
+
// Failed full-history reads must not reassign IDs or expose uncompressed
|
|
538
|
+
// source. Async work from an old session/model cannot publish a projection.
|
|
539
|
+
lastProviderReadyProjectionEpoch = undefined
|
|
540
|
+
const fullBranch = await readDcpJournalBranch(ctx)
|
|
541
|
+
if (state.sessionEpoch !== contextEpoch) throw new DcpJournalError("DCP context owner changed during history read")
|
|
542
|
+
annotateMessagesWithBranchEntryIds(contextMessages, fullBranch)
|
|
452
543
|
const rehydration = rehydrateToolRecordsFromMessages(contextMessages, state)
|
|
453
544
|
if (rehydration.recordsUpdated > 0) {
|
|
454
545
|
writeDcpDebugLog(effectiveConfig, "context.rehydrated_tool_records", { ...rehydration }, ctx)
|
|
@@ -458,6 +549,7 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
458
549
|
// projection must be committed before the request can use them.
|
|
459
550
|
await persistJournalState(ctx, state)
|
|
460
551
|
let candidate = null as ReturnType<typeof detectCompressionCandidate>
|
|
552
|
+
let blockConsolidationCandidate = null as ReturnType<typeof detectCompressionBlockConsolidationCandidate>
|
|
461
553
|
let emergencyCompressionCandidate = null as ReturnType<typeof detectEmergencyCompressionCandidate>
|
|
462
554
|
let messageCandidates = [] as ReturnType<typeof detectMessageCompressionCandidates>
|
|
463
555
|
let emergencySelection = null as ReturnType<typeof analyzeEmergencyCurrentTurn> | null
|
|
@@ -466,8 +558,14 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
466
558
|
// Manual mode skips routine autonomous nudges and automatic summary
|
|
467
559
|
// creation. The bounded emergency safety path remains separate, matching
|
|
468
560
|
// the manual-mode prompt.
|
|
469
|
-
const nativeUsage = normalizeDcpContextUsage(safeGetContextUsage(ctx))
|
|
470
561
|
const ctxModel = (ctx as any).model
|
|
562
|
+
const observedUsage = normalizeDcpContextUsage(safeGetContextUsage(ctx))
|
|
563
|
+
// Model capacity is authoritative even while SDK usage still describes
|
|
564
|
+
// the preceding model. Keep its token count only as a conservative floor.
|
|
565
|
+
const modelWindow = Number.isFinite(ctxModel?.contextWindow) && ctxModel.contextWindow > 0 ? ctxModel.contextWindow : undefined
|
|
566
|
+
const nativeUsage = observedUsage && modelWindow
|
|
567
|
+
? { ...observedUsage, contextWindow: modelWindow, percent: observedUsage.tokens === null ? null : observedUsage.tokens / modelWindow * 100 }
|
|
568
|
+
: observedUsage
|
|
471
569
|
const fallbackContextWindow = nativeUsage?.contextWindow ?? (
|
|
472
570
|
typeof ctxModel?.contextWindow === "number" && Number.isFinite(ctxModel.contextWindow) && ctxModel.contextWindow > 0
|
|
473
571
|
? ctxModel.contextWindow
|
|
@@ -522,23 +620,34 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
522
620
|
estimatorMarginTokens,
|
|
523
621
|
})
|
|
524
622
|
thresholds.maxContextPercent = budget.softHeadroomTokens / usage.contextWindow
|
|
525
|
-
const
|
|
623
|
+
const budgetContextPercent = budget.projectedBeforeTokens / usage.contextWindow
|
|
526
624
|
const nativePressure = emergencyPressureState(
|
|
527
|
-
|
|
625
|
+
budgetContextPercent,
|
|
528
626
|
thresholds.maxContextPercent,
|
|
529
627
|
emergencySettings.hardContextPercent,
|
|
530
628
|
)
|
|
531
629
|
const hardEmergencyReached = nativePressure.hardEmergencyReached || budget.hardPressure
|
|
532
630
|
const contextLimitReached = nativePressure.contextLimitReached || budget.pressured
|
|
533
631
|
const emergencyPressureReached = nativePressure.emergencyPressureReached || budget.pressured
|
|
632
|
+
const routinePressure = routinePressureTracker.estimate(pressureOwner, nativeUsage?.tokens,
|
|
633
|
+
contextMessages, rawContextTokens, repoProjectedTokens, state.compressionBlocks)
|
|
634
|
+
// Correct only routine policy. Capacity, strong pressure and emergency
|
|
635
|
+
// recovery retain the unadjusted conservative provider-native floor.
|
|
636
|
+
const policyTokens = emergencyPressureReached ? budget.projectedBeforeTokens : routinePressure.projectedTokens
|
|
637
|
+
const contextPercent = policyTokens / usage.contextWindow
|
|
534
638
|
const routineNudgesAllowed = contextPercent > thresholds.minContextPercent
|
|
535
639
|
if (!budget.capacityExceeded && state.progressRecovery) state.progressRecovery = undefined
|
|
536
640
|
if (!emergencyPressureReached && !routineNudgesAllowed) {
|
|
537
|
-
|
|
641
|
+
// Falling usage is not an intentional history rewrite. Replay a
|
|
642
|
+
// frozen reminder even after restart or a lower native sample; only
|
|
643
|
+
// discard anchors whose exact carrier is no longer present.
|
|
644
|
+
const anchorsBefore = state.nudgeAnchors.length
|
|
645
|
+
const replay = applyAnchoredNudges(prunedMessages, state, (anchor) => baseNudgeText(anchor.type))
|
|
646
|
+
const clearedAnchors = anchorsBefore - state.nudgeAnchors.length
|
|
538
647
|
const resetEmergencyPasses = state.consecutiveIgnoredStrongNudges > 0 || state.consecutiveIgnoredNudges > 0 || !!state.compressionProgress
|
|
539
648
|
resetCompressionProgress(state)
|
|
540
649
|
warnedProgress.clear()
|
|
541
|
-
if (
|
|
650
|
+
if (replay.stateChanged) await persistJournalState(ctx, state)
|
|
542
651
|
return finishContext("below-threshold", prunedMessages, {
|
|
543
652
|
contextPercent,
|
|
544
653
|
thresholds,
|
|
@@ -578,10 +687,10 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
578
687
|
|
|
579
688
|
const requestedRecoveryTokens = emergencyPressureReached
|
|
580
689
|
? budget.requiredSavingsTokens
|
|
581
|
-
: routineRecoveryTokens(
|
|
690
|
+
: routineRecoveryTokens(policyTokens, usage.contextWindow, thresholds.minContextPercent)
|
|
582
691
|
const progressInput = {
|
|
583
692
|
projectedTokens: repoProjectedTokens,
|
|
584
|
-
observedTokens:
|
|
693
|
+
observedTokens: policyTokens,
|
|
585
694
|
contextWindow: usage.contextWindow,
|
|
586
695
|
requiredTokens: requestedRecoveryTokens,
|
|
587
696
|
kind: emergencyPressureReached ? "emergency" as const : "routine" as const,
|
|
@@ -592,9 +701,11 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
592
701
|
resetCompressionProgress(state)
|
|
593
702
|
warnedProgress.clear()
|
|
594
703
|
}
|
|
595
|
-
const planningRecoveryTokens =
|
|
596
|
-
|
|
597
|
-
|
|
704
|
+
const planningRecoveryTokens = compressionPlanningTokens(
|
|
705
|
+
state,
|
|
706
|
+
progressInput,
|
|
707
|
+
remainingRecoveryTokens,
|
|
708
|
+
)
|
|
598
709
|
const routineEscalated = !state.manualMode && routineNudgesAllowed &&
|
|
599
710
|
state.consecutiveIgnoredNudges > Math.max(0, Math.floor(effectiveConfig.compress.autoCompress.patience))
|
|
600
711
|
const progressPressureReached = emergencyPressureReached || routineEscalated
|
|
@@ -630,7 +741,7 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
630
741
|
effectiveConfig,
|
|
631
742
|
contextPercent,
|
|
632
743
|
progressPressureReached
|
|
633
|
-
? { requiredSavingsTokens: planningRecoveryTokens }
|
|
744
|
+
? { requiredSavingsTokens: planningRecoveryTokens, allowCompressionBlocks: false }
|
|
634
745
|
: undefined,
|
|
635
746
|
)
|
|
636
747
|
messageCandidates = detectMessageCompressionCandidates(
|
|
@@ -639,12 +750,24 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
639
750
|
effectiveConfig,
|
|
640
751
|
contextPercent,
|
|
641
752
|
)
|
|
753
|
+
if (progressPressureReached) {
|
|
754
|
+
blockConsolidationCandidate = detectCompressionBlockConsolidationCandidate(
|
|
755
|
+
prunedMessages,
|
|
756
|
+
state,
|
|
757
|
+
effectiveConfig,
|
|
758
|
+
{
|
|
759
|
+
requiredSavingsTokens: planningRecoveryTokens,
|
|
760
|
+
estimatorMarginTokens,
|
|
761
|
+
},
|
|
762
|
+
)
|
|
763
|
+
}
|
|
642
764
|
writeDcpDebugLog(effectiveConfig, "context.candidates", {
|
|
643
765
|
contextPercent,
|
|
644
766
|
thresholds,
|
|
645
767
|
budget,
|
|
646
768
|
nudgeType,
|
|
647
769
|
candidate,
|
|
770
|
+
blockConsolidationCandidate,
|
|
648
771
|
messageCandidates,
|
|
649
772
|
state: summarizeDcpState(state, effectiveConfig),
|
|
650
773
|
}, ctx)
|
|
@@ -676,7 +799,7 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
676
799
|
effectiveConfig,
|
|
677
800
|
contextPercent,
|
|
678
801
|
planningThreshold,
|
|
679
|
-
{ requiredSavingsTokens: planningRecoveryTokens },
|
|
802
|
+
{ requiredSavingsTokens: planningRecoveryTokens, allowCompressionBlocks: false },
|
|
680
803
|
)
|
|
681
804
|
if (emergencyCompressionCandidate) {
|
|
682
805
|
writeDcpDebugLog(effectiveConfig, "context.emergency_compression_candidate", {
|
|
@@ -732,6 +855,11 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
732
855
|
`DCP cannot safely meet its compression goal: ${blockedReason}. Context has not been recovered.`)
|
|
733
856
|
}
|
|
734
857
|
if (blockedReason && budget.capacityExceeded) {
|
|
858
|
+
warnProgress(
|
|
859
|
+
ctx,
|
|
860
|
+
blockedReason,
|
|
861
|
+
`DCP cannot fit the next provider request safely (${budget.projectedBeforeTokens} projected input tokens > ${budget.inputCapacityTokens} input capacity): ${blockedReason}. The current agent operation is being stopped instead of sending an oversized request.`,
|
|
862
|
+
)
|
|
735
863
|
state.progressRecovery = {
|
|
736
864
|
blockedReason,
|
|
737
865
|
projectedBeforeTokens: budget.projectedBeforeTokens,
|
|
@@ -755,17 +883,17 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
755
883
|
blocked_reason: blockedReason,
|
|
756
884
|
handoff: abortSupported ? "abort-current-agent-operation" : "abort-unavailable",
|
|
757
885
|
budget,
|
|
758
|
-
})
|
|
886
|
+
}, false)
|
|
759
887
|
}
|
|
760
888
|
if (!manualEmergencyOnly && !emergencyPressureReached && !hasCompressionSuggestion) {
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
if (nudgeType
|
|
889
|
+
// Candidate disappearance is not permission to erase a published
|
|
890
|
+
// reminder. Finalization replays it or drops an absent exact carrier.
|
|
891
|
+
if (nudgeType) {
|
|
764
892
|
writeDcpDebugLog(effectiveConfig, "context.no_compression_candidate", {
|
|
765
893
|
contextPercent,
|
|
766
894
|
thresholds,
|
|
767
895
|
nudgeType,
|
|
768
|
-
clearedAnchors,
|
|
896
|
+
clearedAnchors: 0,
|
|
769
897
|
state: summarizeDcpState(state, effectiveConfig),
|
|
770
898
|
}, ctx)
|
|
771
899
|
}
|
|
@@ -781,9 +909,13 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
781
909
|
if (routineEscalated && !effectiveConfig.compress.autoCompress.enabled) {
|
|
782
910
|
warnProgress(ctx, "auto-disabled", "DCP compression has not made enough progress. Automatic compression is disabled; use compress or enable compress.autoCompress explicitly.")
|
|
783
911
|
}
|
|
912
|
+
let autoCompressionFailure: { blockedReason?: DcpBlockedReason; error: string } | undefined
|
|
913
|
+
let partialAutoProjectionTokens: number | undefined
|
|
784
914
|
if (!manualEmergencyOnly) {
|
|
785
|
-
const autoCandidate = candidate ?? emergencyCompressionCandidate
|
|
786
|
-
const cacheSafeReminderAvailable = hasCacheSafeNudgeCarrier(
|
|
915
|
+
const autoCandidate = blockConsolidationCandidate ?? candidate ?? emergencyCompressionCandidate
|
|
916
|
+
const cacheSafeReminderAvailable = hasCacheSafeNudgeCarrier(
|
|
917
|
+
prunedMessages, freshToolResults.eligibleIds, state.nudgeAnchors,
|
|
918
|
+
)
|
|
787
919
|
const autoDecision = decideAutoCompress(
|
|
788
920
|
state,
|
|
789
921
|
effectiveConfig,
|
|
@@ -809,22 +941,44 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
809
941
|
}, ctx)
|
|
810
942
|
}
|
|
811
943
|
if (autoDecision.shouldFire && autoCandidate) {
|
|
944
|
+
const modelSummaryConfigured =
|
|
945
|
+
effectiveConfig.compress.autoCompress.summarizerModel.length > 0 ||
|
|
946
|
+
effectiveConfig.compress.autoCompress.summarizerFallbackModels.length > 0
|
|
812
947
|
try {
|
|
813
948
|
const autoOperationEpoch = state.sessionEpoch
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
|
|
949
|
+
const rawFallbackCandidate = candidate ?? emergencyCompressionCandidate
|
|
950
|
+
const largestSafeCandidate = blockConsolidationCandidate
|
|
951
|
+
? rawFallbackCandidate
|
|
952
|
+
: candidate
|
|
953
|
+
? detectCompressionCandidate(
|
|
954
|
+
prunedMessages,
|
|
955
|
+
state,
|
|
956
|
+
effectiveConfig,
|
|
957
|
+
contextPercent,
|
|
958
|
+
progressPressureReached ? { allowCompressionBlocks: false } : undefined,
|
|
959
|
+
)
|
|
960
|
+
: detectEmergencyCompressionCandidate(
|
|
961
|
+
prunedMessages,
|
|
962
|
+
state,
|
|
963
|
+
effectiveConfig,
|
|
964
|
+
contextPercent,
|
|
965
|
+
planningThreshold,
|
|
966
|
+
{ allowCompressionBlocks: false },
|
|
967
|
+
)
|
|
817
968
|
let preparedProjection: any[] | undefined
|
|
969
|
+
const autoConfig = autoSummarizerDegraded
|
|
970
|
+
? withoutAutoSummaryModels(effectiveConfig)
|
|
971
|
+
: effectiveConfig
|
|
818
972
|
const autoResult = await createBudgetedAutoCompressionBlock({
|
|
819
973
|
candidate: autoCandidate,
|
|
820
974
|
topic: "Auto-compressed slice",
|
|
821
975
|
state,
|
|
822
|
-
config:
|
|
976
|
+
config: autoConfig,
|
|
823
977
|
messages: prunedMessages,
|
|
824
978
|
modelRegistry: (ctx as any).modelRegistry,
|
|
825
979
|
signal: (ctx as any).signal,
|
|
826
980
|
cwd: (ctx as any).cwd,
|
|
827
|
-
requiredGainTokens:
|
|
981
|
+
requiredGainTokens: planningRecoveryTokens,
|
|
828
982
|
allowPartialGain: true,
|
|
829
983
|
persistState: (preparedState, publication) => persistJournalState(ctx, preparedState, publication),
|
|
830
984
|
prepareProjection: (preparedState) => {
|
|
@@ -839,6 +993,21 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
839
993
|
// failed or mutate/nudge the replacement session.
|
|
840
994
|
return { messages: contextMessages }
|
|
841
995
|
}
|
|
996
|
+
if (
|
|
997
|
+
modelSummaryConfigured &&
|
|
998
|
+
!autoSummarizerDegraded &&
|
|
999
|
+
autoResult.summaryMode === "programmatic_fallback"
|
|
1000
|
+
) {
|
|
1001
|
+
// A successful deterministic commit can still prove that every
|
|
1002
|
+
// configured summary model failed for this live session. Avoid paying
|
|
1003
|
+
// the same model timeout on each subsequent autonomous attempt.
|
|
1004
|
+
autoSummarizerDegraded = true
|
|
1005
|
+
writeDcpDebugLog(effectiveConfig, "compress.auto_summarizer_degraded", {
|
|
1006
|
+
reason: "model-summary-fallback",
|
|
1007
|
+
summarizerAttempts: autoResult.summarizerAttempts,
|
|
1008
|
+
nextMode: "deterministic-extractive",
|
|
1009
|
+
}, ctx)
|
|
1010
|
+
}
|
|
842
1011
|
// Re-apply pruning so the new block takes effect on this
|
|
843
1012
|
// same context pass instead of the next one.
|
|
844
1013
|
prunedMessages = preparedProjection ?? applyPruning(contextMessages, state, effectiveConfig)
|
|
@@ -859,46 +1028,71 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
859
1028
|
clearedAnchors,
|
|
860
1029
|
state: summarizeDcpState(state, effectiveConfig),
|
|
861
1030
|
}, ctx)
|
|
862
|
-
|
|
1031
|
+
const projectedAfterAuto = prunedMessages.reduce(
|
|
1032
|
+
(sum, message) => sum + estimateMessageTokens(message),
|
|
1033
|
+
0,
|
|
1034
|
+
)
|
|
1035
|
+
const stillAboveInputCapacity =
|
|
1036
|
+
budget.capacityExceeded && projectedAfterAuto > budget.inputCapacityTokens
|
|
1037
|
+
if (!stillAboveInputCapacity) return finishContext("compress.auto", prunedMessages, {
|
|
863
1038
|
candidate: autoResult.effectiveCandidate,
|
|
864
1039
|
messageCandidates,
|
|
865
1040
|
contextPercent,
|
|
866
1041
|
thresholds,
|
|
867
1042
|
clearedAnchors,
|
|
868
1043
|
})
|
|
1044
|
+
|
|
1045
|
+
// A positive partial commit is useful progress, but it is not permission
|
|
1046
|
+
// to send a request that still exceeds provider input capacity. Continue
|
|
1047
|
+
// through the model-independent recovery floor in this same context pass;
|
|
1048
|
+
// if that cannot make the request fit, the fail-closed handoff below wins.
|
|
1049
|
+
partialAutoProjectionTokens = projectedAfterAuto
|
|
1050
|
+
candidate = null
|
|
1051
|
+
emergencyCompressionCandidate = null
|
|
1052
|
+
if (emergencySettings.enabled) {
|
|
1053
|
+
emergencySelection = analyzeEmergencyCurrentTurn(prunedMessages, state, effectiveConfig)
|
|
1054
|
+
messageCandidates = emergencyCurrentTurnMessageCandidates(emergencySelection, effectiveConfig)
|
|
1055
|
+
}
|
|
1056
|
+
writeDcpDebugLog(effectiveConfig, "compress.auto_partial_capacity", {
|
|
1057
|
+
projectedAfterAuto,
|
|
1058
|
+
inputCapacityTokens: budget.inputCapacityTokens,
|
|
1059
|
+
remainingCapacityGap: projectedAfterAuto - budget.inputCapacityTokens,
|
|
1060
|
+
blockId: `b${autoResult.blockId}`,
|
|
1061
|
+
state: summarizeDcpState(state, effectiveConfig),
|
|
1062
|
+
}, ctx)
|
|
869
1063
|
} catch (error) {
|
|
870
1064
|
if (state.sessionEpoch !== contextEpoch || ctx.signal?.aborted) return { messages: contextMessages }
|
|
1065
|
+
diagnostic("auto-rejected", { reason: error instanceof AutoCompressionBlockedError ? error.blockedReason : "preparation-failed" })
|
|
871
1066
|
const autoBlockedReason = error instanceof AutoCompressionBlockedError
|
|
872
1067
|
? error.blockedReason
|
|
873
1068
|
: undefined
|
|
1069
|
+
const autoError = error instanceof Error ? error.message : String(error)
|
|
1070
|
+
autoCompressionFailure = { blockedReason: autoBlockedReason, error: autoError }
|
|
1071
|
+
const summarizerFailure = error instanceof AutoCompressionBlockedError && (
|
|
1072
|
+
error.blockedReason === "summarizer-unavailable" || error.summarizerFallbackUsed
|
|
1073
|
+
)
|
|
1074
|
+
if (modelSummaryConfigured && !autoSummarizerDegraded && summarizerFailure) {
|
|
1075
|
+
autoSummarizerDegraded = true
|
|
1076
|
+
writeDcpDebugLog(effectiveConfig, "compress.auto_summarizer_degraded", {
|
|
1077
|
+
reason: error.summarizerFallbackUsed ? "model-summary-fallback" : autoBlockedReason ?? "auto-failed",
|
|
1078
|
+
summarizerAttempts: error.summarizerAttempts,
|
|
1079
|
+
nextMode: "deterministic-extractive",
|
|
1080
|
+
}, ctx)
|
|
1081
|
+
}
|
|
874
1082
|
writeDcpDebugLog(effectiveConfig, "compress.auto_failed", {
|
|
875
1083
|
trigger: autoDecision.reason,
|
|
876
1084
|
phase: autoBlockedReason ? "blocked" : "degraded",
|
|
877
1085
|
blocked_reason: autoBlockedReason,
|
|
878
|
-
error:
|
|
1086
|
+
error: autoError,
|
|
1087
|
+
summarizerAttempts: error instanceof AutoCompressionBlockedError ? error.summarizerAttempts : undefined,
|
|
879
1088
|
candidate: autoCandidate,
|
|
880
1089
|
state: summarizeDcpState(state, effectiveConfig),
|
|
881
1090
|
}, ctx)
|
|
882
|
-
|
|
883
|
-
`DCP automatic compression
|
|
884
|
-
if (
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
projectedBeforeTokens: budget.projectedBeforeTokens,
|
|
888
|
-
inputCapacityTokens: budget.inputCapacityTokens,
|
|
889
|
-
requiredSavingsTokens: budget.requiredSavingsTokens,
|
|
890
|
-
contextWindow: budget.contextWindow,
|
|
891
|
-
createdAt: Date.now(),
|
|
892
|
-
}
|
|
893
|
-
clearDcpNudgeAnchors(state)
|
|
894
|
-
await persistJournalState(ctx, state)
|
|
895
|
-
const abortSupported = typeof (ctx as any).abort === "function"
|
|
896
|
-
if (abortSupported) (ctx as any).abort()
|
|
897
|
-
return finishContext("progress.blocked_handoff", prunedMessages, {
|
|
898
|
-
blocked_reason: autoBlockedReason,
|
|
899
|
-
handoff: abortSupported ? "abort-current-agent-operation" : "abort-unavailable",
|
|
900
|
-
budget,
|
|
901
|
-
})
|
|
1091
|
+
warnProgress(ctx, autoBlockedReason ?? "auto-failed",
|
|
1092
|
+
`DCP automatic compression failed (${autoBlockedReason ?? "preparation failed"}). ${modelSummaryConfigured && autoSummarizerDegraded ? "Future attempts in this session will skip the summary model and use deterministic extraction. " : ""}The current context is preserved; emergency pruning will be attempted before any capacity abort.`)
|
|
1093
|
+
if (budget.capacityExceeded && emergencySettings.enabled) {
|
|
1094
|
+
emergencySelection = analyzeEmergencyCurrentTurn(prunedMessages, state, effectiveConfig)
|
|
1095
|
+
messageCandidates = emergencyCurrentTurnMessageCandidates(emergencySelection, effectiveConfig)
|
|
902
1096
|
}
|
|
903
1097
|
// Recoverable failures fall through to normal nudge emission.
|
|
904
1098
|
}
|
|
@@ -914,7 +1108,7 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
914
1108
|
if (
|
|
915
1109
|
emergencySettings.enabled &&
|
|
916
1110
|
emergencyPressureReached &&
|
|
917
|
-
candidate === null &&
|
|
1111
|
+
(candidate === null || autoCompressionFailure !== undefined) &&
|
|
918
1112
|
emergencySelection &&
|
|
919
1113
|
emergencySelection.eligible.length > 0 &&
|
|
920
1114
|
(hardEmergencyReached || emergencyPatienceExceeded)
|
|
@@ -974,6 +1168,55 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
974
1168
|
}
|
|
975
1169
|
}
|
|
976
1170
|
|
|
1171
|
+
if (budget.capacityExceeded && (autoCompressionFailure || partialAutoProjectionTokens !== undefined)) {
|
|
1172
|
+
const recoveredTokens = emergencyPruneResult?.estimatedTokensRecovered ?? 0
|
|
1173
|
+
const projectedAfterRecovery = prunedMessages.reduce(
|
|
1174
|
+
(sum, message) => sum + estimateMessageTokens(message),
|
|
1175
|
+
0,
|
|
1176
|
+
)
|
|
1177
|
+
if (projectedAfterRecovery > budget.inputCapacityTokens) {
|
|
1178
|
+
const finalBlockedReason = autoCompressionFailure?.blockedReason ?? "budget-exhausted"
|
|
1179
|
+
const capacityGap = projectedAfterRecovery - budget.inputCapacityTokens
|
|
1180
|
+
state.progressRecovery = {
|
|
1181
|
+
blockedReason: finalBlockedReason,
|
|
1182
|
+
projectedBeforeTokens: projectedAfterRecovery,
|
|
1183
|
+
inputCapacityTokens: budget.inputCapacityTokens,
|
|
1184
|
+
requiredSavingsTokens: capacityGap,
|
|
1185
|
+
contextWindow: budget.contextWindow,
|
|
1186
|
+
createdAt: Date.now(),
|
|
1187
|
+
}
|
|
1188
|
+
clearDcpNudgeAnchors(state)
|
|
1189
|
+
await persistJournalState(ctx, state)
|
|
1190
|
+
warnProgress(
|
|
1191
|
+
ctx,
|
|
1192
|
+
finalBlockedReason,
|
|
1193
|
+
autoCompressionFailure
|
|
1194
|
+
? `DCP recovery could not make the next request fit safely after auto-compression failed (${autoCompressionFailure.error}). ${projectedAfterRecovery} projected input tokens still exceed ${budget.inputCapacityTokens} input capacity by ${capacityGap}; the current agent operation is being stopped.`
|
|
1195
|
+
: `DCP partial auto-compression made progress but the next request still does not fit safely. ${projectedAfterRecovery} projected input tokens exceed ${budget.inputCapacityTokens} input capacity by ${capacityGap}; the current agent operation is being stopped instead of sending an oversized request.`,
|
|
1196
|
+
)
|
|
1197
|
+
const abortSupported = typeof (ctx as any).abort === "function"
|
|
1198
|
+
if (abortSupported) (ctx as any).abort()
|
|
1199
|
+
writeDcpDebugLog(effectiveConfig, "context.progress_handoff", {
|
|
1200
|
+
phase: "blocked",
|
|
1201
|
+
blocked_reason: finalBlockedReason,
|
|
1202
|
+
autoCompressionFailure,
|
|
1203
|
+
recoveredTokens,
|
|
1204
|
+
projectedAfterRecovery,
|
|
1205
|
+
handoff: abortSupported ? "abort-current-agent-operation" : "abort-unavailable",
|
|
1206
|
+
budget,
|
|
1207
|
+
state: summarizeDcpState(state, effectiveConfig),
|
|
1208
|
+
}, ctx)
|
|
1209
|
+
return finishContext("progress.blocked_handoff", prunedMessages, {
|
|
1210
|
+
blocked_reason: finalBlockedReason,
|
|
1211
|
+
autoCompressionFailure,
|
|
1212
|
+
recoveredTokens,
|
|
1213
|
+
projectedAfterRecovery,
|
|
1214
|
+
handoff: abortSupported ? "abort-current-agent-operation" : "abort-unavailable",
|
|
1215
|
+
budget,
|
|
1216
|
+
}, false)
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
977
1220
|
if (nudgeType && !manualEmergencyOnly && (hasCompressionSuggestion || emergencyPressureReached)) {
|
|
978
1221
|
latestProviderOpportunityAvailable = latestProviderOpportunityKind !== undefined
|
|
979
1222
|
const nudgeText = appendConcreteNudgeGuidance(
|
|
@@ -987,7 +1230,7 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
987
1230
|
prunedMessages,
|
|
988
1231
|
state,
|
|
989
1232
|
nudgeType,
|
|
990
|
-
{ contextPercent, renderedReminder: nudgeText },
|
|
1233
|
+
{ contextPercent, renderedReminder: nudgeText, freshToolResultIds: freshToolResults.eligibleIds },
|
|
991
1234
|
)
|
|
992
1235
|
if (anchorResult.anchor) {
|
|
993
1236
|
if (anchorResult.updated) {
|
|
@@ -995,7 +1238,7 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
995
1238
|
anchorResult.created ? "emitted" : "upgraded",
|
|
996
1239
|
nudgeType,
|
|
997
1240
|
anchorResult.anchor,
|
|
998
|
-
usage,
|
|
1241
|
+
{ ...usage, tokens: policyTokens, percent: contextPercent * 100 },
|
|
999
1242
|
toolCallsSinceLastUser,
|
|
1000
1243
|
)
|
|
1001
1244
|
await persistJournalState(ctx, state)
|
|
@@ -1011,15 +1254,15 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
1011
1254
|
"reapplied",
|
|
1012
1255
|
anchorResult.anchor.type,
|
|
1013
1256
|
anchorResult.anchor,
|
|
1014
|
-
usage,
|
|
1257
|
+
{ ...usage, tokens: policyTokens, percent: contextPercent * 100 },
|
|
1015
1258
|
toolCallsSinceLastUser,
|
|
1016
1259
|
)
|
|
1017
1260
|
}
|
|
1018
1261
|
} else {
|
|
1019
1262
|
// Do not rewrite an already-sent user message and do not create an
|
|
1020
1263
|
// ephemeral synthetic tail that disappears from the next provider
|
|
1021
|
-
// continuation.
|
|
1022
|
-
// pressure is handled by the bounded emergency path above.
|
|
1264
|
+
// continuation. Wait for a fresh user or proven-local tool result;
|
|
1265
|
+
// hard pressure is handled by the bounded emergency path above.
|
|
1023
1266
|
writeDcpDebugLog(effectiveConfig, "nudge.deferred_for_cache", {
|
|
1024
1267
|
type: nudgeType,
|
|
1025
1268
|
contextPercent,
|
|
@@ -1069,12 +1312,36 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
1069
1312
|
|
|
1070
1313
|
// ── 10b. provider lifecycle evidence ─────────────────────────────────────
|
|
1071
1314
|
pi.on("before_provider_request", async (event, ctx) => {
|
|
1315
|
+
// Consume grants even on failed, retried, omitted-result or disabled sends.
|
|
1316
|
+
// A completed-provider-response check here would be too late for caching.
|
|
1317
|
+
freshToolResults.reset()
|
|
1072
1318
|
const effectiveConfig = configForContext(ctx)
|
|
1073
1319
|
if (!effectiveConfig.enabled) {
|
|
1074
1320
|
providerEvidenceTracker.reset()
|
|
1075
1321
|
return undefined
|
|
1076
1322
|
}
|
|
1323
|
+
if (journalBlockedReason) throw new DcpJournalError(`DCP journal is blocked: ${journalBlockedReason}`)
|
|
1324
|
+
if (isJournalSessionSupported() && lastProviderReadyProjectionEpoch !== state.sessionEpoch) {
|
|
1325
|
+
const message =
|
|
1326
|
+
`DCP blocked a provider request for session epoch ${state.sessionEpoch} because no provider-ready context projection ` +
|
|
1327
|
+
"has completed in this epoch. This can happen after a session lifecycle reset between context construction and provider send; " +
|
|
1328
|
+
"the raw session history will not be sent. Retry the turn so context can be rebuilt safely."
|
|
1329
|
+
warnProgress(ctx, "stale-context-projection", message)
|
|
1330
|
+
const abortSupported = typeof (ctx as any).abort === "function"
|
|
1331
|
+
writeDcpDebugLog(effectiveConfig, "provider_payload.blocked_stale_projection", {
|
|
1332
|
+
sessionEpoch: state.sessionEpoch,
|
|
1333
|
+
lastProviderReadyProjectionEpoch,
|
|
1334
|
+
handoff: abortSupported ? "abort-current-agent-operation" : "throw-before-provider-request",
|
|
1335
|
+
state: summarizeDcpState(state, effectiveConfig),
|
|
1336
|
+
}, ctx)
|
|
1337
|
+
if (abortSupported) {
|
|
1338
|
+
(ctx as any).abort()
|
|
1339
|
+
return undefined
|
|
1340
|
+
}
|
|
1341
|
+
throw new Error(message)
|
|
1342
|
+
}
|
|
1077
1343
|
|
|
1344
|
+
freshToolResults.beforeProviderRequest()
|
|
1078
1345
|
const providerEvidence = collectProviderToolResultEvidence(event.payload)
|
|
1079
1346
|
const pendingToolIds = new Set<string>()
|
|
1080
1347
|
for (const meta of state.messageMetaSnapshot.values()) {
|
|
@@ -1099,6 +1366,8 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
1099
1366
|
opportunityAvailable: reminderDelivered,
|
|
1100
1367
|
opportunityKind: reminderDelivered ? latestProviderOpportunityKind : undefined,
|
|
1101
1368
|
})
|
|
1369
|
+
routinePressureTracker.beforeRequest()
|
|
1370
|
+
diagnostic("request", { id: `${diagnosticEpoch}:${pending.lastAttemptId}`, reminder: reminderDelivered, snapshot: diagnosticSnapshot })
|
|
1102
1371
|
|
|
1103
1372
|
writeDcpDebugLog(effectiveConfig, "provider_payload.message_ids", {
|
|
1104
1373
|
injected: false,
|
|
@@ -1152,6 +1421,7 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
1152
1421
|
model: typeof message.model === "string" ? message.model : undefined,
|
|
1153
1422
|
stopReason: typeof message.stopReason === "string" ? message.stopReason : undefined,
|
|
1154
1423
|
})
|
|
1424
|
+
routinePressureTracker.complete(message, completion.status === "promote")
|
|
1155
1425
|
latestProviderOpportunityAvailable = false
|
|
1156
1426
|
|
|
1157
1427
|
if (completion.status !== "promote") {
|
|
@@ -1213,6 +1483,8 @@ export default async function dcpModule(pi: ExtensionAPI, dependencies: { config
|
|
|
1213
1483
|
state.providerSeenToolIds = workingState.providerSeenToolIds
|
|
1214
1484
|
state.consecutiveIgnoredStrongNudges = workingState.consecutiveIgnoredStrongNudges
|
|
1215
1485
|
state.consecutiveIgnoredNudges = workingState.consecutiveIgnoredNudges
|
|
1486
|
+
diagnostic("completion", { id: `${diagnosticEpoch}:${completion.lastAttemptId}`,
|
|
1487
|
+
reminder: ignoredCompressionOpportunity, ignored: state.consecutiveIgnoredNudges })
|
|
1216
1488
|
writeDcpDebugLog(effectiveConfig, "provider_payload.tool_results_seen", {
|
|
1217
1489
|
attempts: completion.attempts,
|
|
1218
1490
|
newlySeenToolResults: newlySeen.length,
|