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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type ContextGatewayMode = "off" | "observe" | "enforce";
|
|
2
2
|
|
|
3
|
-
export type ContextGatewayEffectiveMode =
|
|
3
|
+
export type ContextGatewayEffectiveMode = ContextGatewayMode;
|
|
4
4
|
|
|
5
5
|
export interface ContextGatewayBudgets {
|
|
6
6
|
maxInlineBytes: number;
|
|
@@ -10,10 +10,17 @@ export interface ContextGatewayBudgets {
|
|
|
10
10
|
maxSearchMatches: number;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
export interface ContextGatewayAccountingLogConfig {
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
maxBytes: number;
|
|
16
|
+
maxBackups: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Parsed layered config. `enforce` is selective and fail-open for unsupported result classes. */
|
|
14
20
|
export interface ContextGatewayResolvedConfig {
|
|
15
21
|
mode: ContextGatewayMode;
|
|
16
22
|
budgets: ContextGatewayBudgets;
|
|
23
|
+
accountingLog: ContextGatewayAccountingLogConfig;
|
|
17
24
|
issues: string[];
|
|
18
25
|
}
|
|
19
26
|
|
|
@@ -48,7 +55,7 @@ export interface ContextGatewayObservedView {
|
|
|
48
55
|
}
|
|
49
56
|
|
|
50
57
|
export interface ContextGatewayObservedDelivery {
|
|
51
|
-
representation: "passthrough";
|
|
58
|
+
representation: "passthrough" | "test-build-compact" | "web-recoverable-compact";
|
|
52
59
|
contentBytes: number;
|
|
53
60
|
textBytes: number;
|
|
54
61
|
}
|
|
@@ -81,11 +88,13 @@ export interface ContextGatewayObservation {
|
|
|
81
88
|
version: 1;
|
|
82
89
|
toolClass: ContextGatewayToolClass;
|
|
83
90
|
outcome: ContextGatewayExecutionOutcome;
|
|
91
|
+
budgetBytes: number;
|
|
84
92
|
source: ContextGatewayObservedSource;
|
|
85
93
|
view: ContextGatewayObservedView;
|
|
86
94
|
delivery: ContextGatewayObservedDelivery;
|
|
87
95
|
overBudget: boolean;
|
|
88
96
|
potentialBytesOverBudget: number;
|
|
97
|
+
actualBytesSaved: number;
|
|
89
98
|
testOutput?: ContextGatewayTestOutputObservation;
|
|
90
99
|
}
|
|
91
100
|
|
|
@@ -93,12 +102,15 @@ export interface ContextGatewayClassTelemetry {
|
|
|
93
102
|
results: number;
|
|
94
103
|
errors: number;
|
|
95
104
|
contentBytes: number;
|
|
105
|
+
deliveredContentBytes: number;
|
|
96
106
|
textBytes: number;
|
|
97
107
|
imageBytes: number;
|
|
98
108
|
detailsBytes: number;
|
|
99
109
|
upstreamTruncatedResults: number;
|
|
100
110
|
overBudgetResults: number;
|
|
101
111
|
potentialBytesOverBudget: number;
|
|
112
|
+
enforcedResults: number;
|
|
113
|
+
actualBytesSaved: number;
|
|
102
114
|
}
|
|
103
115
|
|
|
104
116
|
export type ContextGatewayNativePolicyReason =
|
|
@@ -134,6 +146,7 @@ export interface ContextGatewayTelemetrySnapshot extends ContextGatewayClassTele
|
|
|
134
146
|
pendingCalls: number;
|
|
135
147
|
unboundResults: number;
|
|
136
148
|
repeatCandidateCount: number;
|
|
149
|
+
sameSourceDifferentRangeCount: number;
|
|
137
150
|
retrievalCalls: number;
|
|
138
151
|
nativePolicy: ContextGatewayNativePolicyTelemetry;
|
|
139
152
|
testOutput: ContextGatewayTestOutputTelemetry;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import {
|
|
3
|
+
currentModelRef,
|
|
4
|
+
filterSubagentConfigForParentModel,
|
|
5
|
+
loadSubagentConfig,
|
|
6
|
+
} from "./async-subagents/lib.js";
|
|
7
|
+
import { publishRpcSessionState } from "./lib/rpc-session-state.js";
|
|
8
|
+
|
|
9
|
+
export const CONTEXT_INVENTORY_EVENT = "pi-tools-suite:context-inventory";
|
|
10
|
+
|
|
11
|
+
export interface ContextInventoryState {
|
|
12
|
+
version: 1;
|
|
13
|
+
reason?: "startup" | "reload" | "new" | "resume" | "fork" | "model_select";
|
|
14
|
+
sessionId?: string;
|
|
15
|
+
sessionFile?: string;
|
|
16
|
+
model?: string;
|
|
17
|
+
thinking?: string;
|
|
18
|
+
tools: string[];
|
|
19
|
+
skills: string[];
|
|
20
|
+
agents?: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function createContextInventoryState(
|
|
24
|
+
pi: Pick<ExtensionAPI, "getActiveTools" | "getCommands">,
|
|
25
|
+
ctx: Pick<ExtensionContext, "cwd" | "model" | "sessionManager">,
|
|
26
|
+
reason?: ContextInventoryState["reason"],
|
|
27
|
+
): ContextInventoryState {
|
|
28
|
+
const tools = unique(pi.getActiveTools());
|
|
29
|
+
const skillsReadable = tools.includes("read") || tools.includes("bash");
|
|
30
|
+
const skills = skillsReadable
|
|
31
|
+
? unique(pi.getCommands()
|
|
32
|
+
.filter((command) => command.source === "skill")
|
|
33
|
+
.map((command) => command.name.replace(/^skill:/u, "")))
|
|
34
|
+
.sort(compareText)
|
|
35
|
+
: [];
|
|
36
|
+
const model = currentModelRef(ctx.model);
|
|
37
|
+
const thinking = typeof (ctx as { thinkingLevel?: unknown }).thinkingLevel === "string"
|
|
38
|
+
? (ctx as { thinkingLevel?: string }).thinkingLevel?.trim() || undefined
|
|
39
|
+
: undefined;
|
|
40
|
+
const agents = tools.includes("subagents") ? effectiveAgentTypes(ctx.cwd, model) : [];
|
|
41
|
+
const sessionId = safeSessionId(ctx.sessionManager);
|
|
42
|
+
const sessionFile = safeSessionFile(ctx.sessionManager);
|
|
43
|
+
return {
|
|
44
|
+
version: 1,
|
|
45
|
+
...(reason ? { reason } : {}),
|
|
46
|
+
...(sessionId ? { sessionId } : {}),
|
|
47
|
+
...(sessionFile ? { sessionFile } : {}),
|
|
48
|
+
...(model ? { model } : {}),
|
|
49
|
+
...(thinking ? { thinking } : {}),
|
|
50
|
+
tools,
|
|
51
|
+
skills,
|
|
52
|
+
...(agents === undefined ? {} : { agents }),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function publishContextInventoryState(
|
|
57
|
+
pi: ExtensionAPI,
|
|
58
|
+
ctx: ExtensionContext,
|
|
59
|
+
reason?: ContextInventoryState["reason"],
|
|
60
|
+
): void {
|
|
61
|
+
const state = createContextInventoryState(pi, ctx, reason);
|
|
62
|
+
pi.events?.emit?.(CONTEXT_INVENTORY_EVENT, state);
|
|
63
|
+
publishRpcSessionState(ctx, CONTEXT_INVENTORY_EVENT, state);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function effectiveAgentTypes(cwd: string, model: string | undefined): string[] | undefined {
|
|
67
|
+
try {
|
|
68
|
+
const config = filterSubagentConfigForParentModel(loadSubagentConfig(cwd), model);
|
|
69
|
+
return Object.keys(config.types).sort(compareText);
|
|
70
|
+
} catch {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function safeSessionId(sessionManager: ExtensionContext["sessionManager"]): string | undefined {
|
|
76
|
+
try {
|
|
77
|
+
const sessionId = sessionManager.getSessionId();
|
|
78
|
+
return sessionId?.trim() || undefined;
|
|
79
|
+
} catch {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function safeSessionFile(sessionManager: ExtensionContext["sessionManager"]): string | undefined {
|
|
85
|
+
try {
|
|
86
|
+
const sessionFile = sessionManager.getSessionFile();
|
|
87
|
+
return sessionFile?.trim() || undefined;
|
|
88
|
+
} catch {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function unique(values: readonly string[]): string[] {
|
|
94
|
+
return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function compareText(left: string, right: string): number {
|
|
98
|
+
return left.localeCompare(right);
|
|
99
|
+
}
|
|
@@ -5,13 +5,32 @@ import {
|
|
|
5
5
|
type AutoCompressionResult,
|
|
6
6
|
type CreateAutoCompressionBlockOptions,
|
|
7
7
|
} from "./auto-compress.js";
|
|
8
|
-
import { canonicalMessageHash } from "./conversation-index.js";
|
|
8
|
+
import { buildExactRangeMembership, canonicalMessageHash } from "./conversation-index.js";
|
|
9
9
|
import { stableMessageKeys } from "./pruner-message-ids.js";
|
|
10
10
|
import type { CompressionCandidate } from "./pruner-types.js";
|
|
11
11
|
import type { DcpState } from "./state.js";
|
|
12
12
|
|
|
13
13
|
const rejectedSources = new WeakMap<DcpState, { key: string; error: AutoCompressionBlockedError }>();
|
|
14
14
|
const FINALIZATION_GRACE_MS = 1_000;
|
|
15
|
+
const SUMMARY_DEADLINE_FRACTION = 0.75;
|
|
16
|
+
|
|
17
|
+
function exactCandidateFingerprint(
|
|
18
|
+
options: CreateAutoCompressionBlockOptions,
|
|
19
|
+
candidate: CompressionCandidate,
|
|
20
|
+
): { startId: string; endId: string; sourceMembers: Array<{ stableId: string; hash: string }> } | undefined {
|
|
21
|
+
const membership = buildExactRangeMembership(
|
|
22
|
+
options.state.conversationIndexSnapshot,
|
|
23
|
+
candidate.startId,
|
|
24
|
+
candidate.endId,
|
|
25
|
+
options.state,
|
|
26
|
+
);
|
|
27
|
+
if (!membership) return undefined;
|
|
28
|
+
return {
|
|
29
|
+
startId: candidate.startId,
|
|
30
|
+
endId: candidate.endId,
|
|
31
|
+
sourceMembers: membership.sourceMembers,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
15
34
|
|
|
16
35
|
/**
|
|
17
36
|
* A source-size estimate is not a net-savings estimate. Try the economical
|
|
@@ -25,14 +44,28 @@ export async function createBudgetedAutoCompressionBlock(
|
|
|
25
44
|
): Promise<AutoCompressionResult> {
|
|
26
45
|
options.signal?.throwIfAborted();
|
|
27
46
|
const epoch = options.state.sessionEpoch;
|
|
47
|
+
const primaryFingerprint = exactCandidateFingerprint(options, options.candidate);
|
|
48
|
+
const largestFingerprint = largestSafeCandidate
|
|
49
|
+
? exactCandidateFingerprint(options, largestSafeCandidate)
|
|
50
|
+
: undefined;
|
|
51
|
+
// Rejection memoization belongs to the exact source that was rejected, not
|
|
52
|
+
// to the whole provider tail. A long-running agent can append unrelated
|
|
53
|
+
// messages while the oldest safe candidate stays byte-identical; hashing the
|
|
54
|
+
// whole tail made that same impossible candidate run again after every tool
|
|
55
|
+
// result. Fall back to the full-context identity only when exact membership
|
|
56
|
+
// is unavailable, so memoization never guesses source equivalence.
|
|
57
|
+
const sourceIdentity = primaryFingerprint && (!largestSafeCandidate || largestFingerprint)
|
|
58
|
+
? { primary: primaryFingerprint, largest: largestFingerprint }
|
|
59
|
+
: {
|
|
60
|
+
source: options.messages.map(canonicalMessageHash),
|
|
61
|
+
ids: stableMessageKeys(options.messages),
|
|
62
|
+
};
|
|
28
63
|
const key = createHash("sha256").update(JSON.stringify({
|
|
29
64
|
epoch,
|
|
30
|
-
|
|
31
|
-
ids: stableMessageKeys(options.messages),
|
|
65
|
+
sourceIdentity,
|
|
32
66
|
config: options.config,
|
|
33
67
|
budget: options.requiredGainTokens ?? 0,
|
|
34
68
|
allowPartialGain: options.allowPartialGain === true,
|
|
35
|
-
largest: largestSafeCandidate,
|
|
36
69
|
})).digest("hex");
|
|
37
70
|
const rejected = rejectedSources.get(options.state);
|
|
38
71
|
if (rejected?.key === key) throw rejected.error;
|
|
@@ -41,6 +74,7 @@ export async function createBudgetedAutoCompressionBlock(
|
|
|
41
74
|
const onAbort = () => controller.abort(options.signal?.reason);
|
|
42
75
|
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
43
76
|
const timeout = Math.max(1, options.config.compress.autoCompress.timeoutMs);
|
|
77
|
+
const summaryTimeoutMs = Math.max(1, Math.floor(timeout * SUMMARY_DEADLINE_FRACTION));
|
|
44
78
|
// generateModelSummary owns the configured summarizer deadline and can
|
|
45
79
|
// deliberately recover from it with the deterministic extractive fallback.
|
|
46
80
|
// Keep a small bounded tail for that fallback and durable publication;
|
|
@@ -84,6 +118,7 @@ export async function createBudgetedAutoCompressionBlock(
|
|
|
84
118
|
...options,
|
|
85
119
|
candidate: candidates[attempt]!,
|
|
86
120
|
signal: controller.signal,
|
|
121
|
+
summaryTimeoutMs,
|
|
87
122
|
});
|
|
88
123
|
rejectedSources.delete(options.state);
|
|
89
124
|
return result;
|
|
@@ -16,7 +16,7 @@ import { createHash } from "node:crypto"
|
|
|
16
16
|
import type { Model, Api, ProviderHeaders } from "@earendil-works/pi-ai"
|
|
17
17
|
import { completeWithModelRegistry, type ModelCompletionRegistry } from "../model-completion.js"
|
|
18
18
|
import type { DcpState } from "./state.js"
|
|
19
|
-
import type
|
|
19
|
+
import { summarizerModelRefs, type DcpConfig } from "./config.js"
|
|
20
20
|
import type { CompressionCandidate } from "./pruner-types.js"
|
|
21
21
|
import { estimateMessageTokens, estimateTokens, stripStaleDcpMetadataLines } from "./pruner-metadata.js"
|
|
22
22
|
import {
|
|
@@ -37,11 +37,19 @@ import { settleCompressionProgress } from "./compression-progress.js"
|
|
|
37
37
|
|
|
38
38
|
export class AutoCompressionBlockedError extends Error {
|
|
39
39
|
readonly blockedReason: DcpBlockedReason
|
|
40
|
+
readonly summarizerFallbackUsed: boolean
|
|
41
|
+
readonly summarizerAttempts?: ModelSummaryAttempt[]
|
|
40
42
|
|
|
41
|
-
constructor(
|
|
43
|
+
constructor(
|
|
44
|
+
blockedReason: DcpBlockedReason,
|
|
45
|
+
message: string,
|
|
46
|
+
details: { summarizerFallbackUsed?: boolean; summarizerAttempts?: ModelSummaryAttempt[] } = {},
|
|
47
|
+
) {
|
|
42
48
|
super(message)
|
|
43
49
|
this.name = "AutoCompressionBlockedError"
|
|
44
50
|
this.blockedReason = blockedReason
|
|
51
|
+
this.summarizerFallbackUsed = details.summarizerFallbackUsed === true
|
|
52
|
+
this.summarizerAttempts = details.summarizerAttempts
|
|
45
53
|
}
|
|
46
54
|
}
|
|
47
55
|
|
|
@@ -775,6 +783,80 @@ export async function generateModelSummary(
|
|
|
775
783
|
return { attempts }
|
|
776
784
|
}
|
|
777
785
|
|
|
786
|
+
export interface PreparedCompressionSummary {
|
|
787
|
+
text: string
|
|
788
|
+
representation: "model" | "extractive" | "extractive-fallback"
|
|
789
|
+
sourceManifest: SummarySourceItem[]
|
|
790
|
+
sourceHash: string
|
|
791
|
+
sourceCoverage: SummarySourceCoverage
|
|
792
|
+
summarizerModelRef?: string
|
|
793
|
+
summarizerAttempts?: ModelSummaryAttempt[]
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Shared summary preparation for automatic and explicit compression. The
|
|
798
|
+
* configured model chain is attempted first; if it is absent/unavailable the
|
|
799
|
+
* deterministic extractive continuation record is the safety floor.
|
|
800
|
+
*/
|
|
801
|
+
export async function prepareCompressionSummary(options: {
|
|
802
|
+
topic: string
|
|
803
|
+
messages: any[]
|
|
804
|
+
candidate?: CompressionCandidate
|
|
805
|
+
modelRefs: string[]
|
|
806
|
+
timeoutMs: number
|
|
807
|
+
modelRegistry?: ModelSummaryRegistry
|
|
808
|
+
signal?: AbortSignal
|
|
809
|
+
sourceManifest?: SummarySourceItem[]
|
|
810
|
+
}): Promise<PreparedCompressionSummary> {
|
|
811
|
+
options.signal?.throwIfAborted()
|
|
812
|
+
const sourceManifest = options.sourceManifest ?? buildSummarySourceManifest(options.messages)
|
|
813
|
+
let modelResult: ModelSummaryResult | undefined
|
|
814
|
+
if (options.modelRefs.length > 0) {
|
|
815
|
+
modelResult = await generateModelSummary(
|
|
816
|
+
options.modelRefs,
|
|
817
|
+
options.modelRegistry,
|
|
818
|
+
options.signal,
|
|
819
|
+
options.topic,
|
|
820
|
+
options.messages,
|
|
821
|
+
options.timeoutMs,
|
|
822
|
+
sourceManifest,
|
|
823
|
+
)
|
|
824
|
+
options.signal?.throwIfAborted()
|
|
825
|
+
}
|
|
826
|
+
const fallbackCandidate = options.candidate ?? {
|
|
827
|
+
startId: "generated",
|
|
828
|
+
endId: "generated",
|
|
829
|
+
messageCount: sourceManifest.length,
|
|
830
|
+
estimatedTokens: options.messages.reduce((sum, message) => sum + estimateMessageTokens(message), 0),
|
|
831
|
+
includedBlockIds: [],
|
|
832
|
+
reason: "explicit/generated summary source",
|
|
833
|
+
}
|
|
834
|
+
const text = modelResult?.text ?? buildExtractiveSummary(
|
|
835
|
+
options.topic,
|
|
836
|
+
fallbackCandidate,
|
|
837
|
+
sourceManifest,
|
|
838
|
+
)
|
|
839
|
+
// Do not impose a second, fixed output ceiling on the deterministic safety
|
|
840
|
+
// floor. A large source can legitimately require more than 8192 tokens to
|
|
841
|
+
// preserve every explicit continuity checkpoint. The auto-compression path
|
|
842
|
+
// already measures the actual replacement against the exact source and
|
|
843
|
+
// rejects non-positive/full-projection gain below, so compression economics
|
|
844
|
+
// — not an unrelated constant — decide whether this fallback is usable.
|
|
845
|
+
return {
|
|
846
|
+
text,
|
|
847
|
+
representation: modelResult?.text
|
|
848
|
+
? "model"
|
|
849
|
+
: options.modelRefs.length > 0
|
|
850
|
+
? "extractive-fallback"
|
|
851
|
+
: "extractive",
|
|
852
|
+
sourceManifest,
|
|
853
|
+
sourceHash: hashSummarySourceManifest(sourceManifest),
|
|
854
|
+
sourceCoverage: summarySourceCoverage(sourceManifest),
|
|
855
|
+
summarizerModelRef: modelResult?.usedModelRef,
|
|
856
|
+
summarizerAttempts: modelResult?.attempts.length ? modelResult.attempts : undefined,
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
778
860
|
function extractAssistantText(result: any): string | undefined {
|
|
779
861
|
const content = result?.content
|
|
780
862
|
if (!Array.isArray(content)) return undefined
|
|
@@ -807,6 +889,8 @@ export interface CreateAutoCompressionBlockOptions {
|
|
|
807
889
|
requiredGainTokens?: number
|
|
808
890
|
/** Allow a positive safe commit that reduces, but does not fully settle, the current recovery debt. */
|
|
809
891
|
allowPartialGain?: boolean
|
|
892
|
+
/** Optional model-summary sub-deadline. The outer budgeted operation may reserve time for deterministic fallback/publication. */
|
|
893
|
+
summaryTimeoutMs?: number
|
|
810
894
|
/** Optional durable publication hook. Live state is not changed unless it succeeds. */
|
|
811
895
|
persistState?: (preparedState: DcpState, publication?: DcpJournalPublicationOptions) => Promise<void>
|
|
812
896
|
/** Optional pure projection preparation; included in the same durable generation. */
|
|
@@ -845,8 +929,9 @@ function createAutoCompressionWorkingState(state: DcpState): DcpState {
|
|
|
845
929
|
|
|
846
930
|
/**
|
|
847
931
|
* Create the auto-compression block. Selects the summary source based on
|
|
848
|
-
* `config.compress.autoCompress.summarizerModel
|
|
849
|
-
*
|
|
932
|
+
* `config.compress.autoCompress.summarizerModel` plus explicit
|
|
933
|
+
* `summarizerFallbackModels`: empty → programmatic digest; non-empty → model
|
|
934
|
+
* summary with ordered model fallbacks, then programmatic fallback. Delegates block
|
|
850
935
|
* creation to the shared `createRangeCompressionBlock` path so protected
|
|
851
936
|
* content (user messages, tool outputs, prompt info) is handled identically to
|
|
852
937
|
* a model-initiated compress.
|
|
@@ -913,48 +998,26 @@ export async function createAutoCompressionBlock(
|
|
|
913
998
|
}
|
|
914
999
|
}
|
|
915
1000
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
const
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
settings.timeoutMs,
|
|
937
|
-
sourceManifest,
|
|
938
|
-
)
|
|
939
|
-
assertCurrent()
|
|
940
|
-
summarizerAttempts = modelResult.attempts.length > 0 ? modelResult.attempts : undefined
|
|
941
|
-
if (modelResult.text) {
|
|
942
|
-
summary = modelResult.text
|
|
943
|
-
summaryMode = "model"
|
|
944
|
-
summarizerModelRef = modelResult.usedModelRef
|
|
945
|
-
} else {
|
|
946
|
-
// All configured models failed or returned empty — fall back to the
|
|
947
|
-
// programmatic digest, but mark the mode distinctly so the fallback
|
|
948
|
-
// is visible in DCP debug logs.
|
|
949
|
-
summaryMode = "programmatic_fallback"
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
if (!summary) {
|
|
953
|
-
summary = buildExtractiveSummary(topic, effectiveCandidate, sourceManifest)
|
|
954
|
-
if (estimateTokens(summary) > 8192) {
|
|
955
|
-
throw new AutoCompressionBlockedError("budget-exhausted", "Extractive continuity minimum exceeds its 8192-token budget; no checkpoints were silently dropped")
|
|
956
|
-
}
|
|
957
|
-
}
|
|
1001
|
+
const preparedSummary = await prepareCompressionSummary({
|
|
1002
|
+
topic,
|
|
1003
|
+
messages: messagesInRange,
|
|
1004
|
+
candidate: effectiveCandidate,
|
|
1005
|
+
modelRefs: summarizerModelRefs(settings),
|
|
1006
|
+
timeoutMs: options.summaryTimeoutMs ?? settings.timeoutMs,
|
|
1007
|
+
modelRegistry,
|
|
1008
|
+
signal,
|
|
1009
|
+
})
|
|
1010
|
+
assertCurrent()
|
|
1011
|
+
const summary = preparedSummary.text
|
|
1012
|
+
const sourceManifest = preparedSummary.sourceManifest
|
|
1013
|
+
const summaryMode: "programmatic" | "model" | "programmatic_fallback" =
|
|
1014
|
+
preparedSummary.representation === "model"
|
|
1015
|
+
? "model"
|
|
1016
|
+
: preparedSummary.representation === "extractive-fallback"
|
|
1017
|
+
? "programmatic_fallback"
|
|
1018
|
+
: "programmatic"
|
|
1019
|
+
const summarizerModelRef = preparedSummary.summarizerModelRef
|
|
1020
|
+
const summarizerAttempts = preparedSummary.summarizerAttempts
|
|
958
1021
|
|
|
959
1022
|
const workingState = createAutoCompressionWorkingState(state)
|
|
960
1023
|
const anchor = resolveAnchorBoundary(endTimestamp, workingState, endBoundary.stableId)
|
|
@@ -1004,8 +1067,8 @@ export async function createAutoCompressionBlock(
|
|
|
1004
1067
|
: summaryMode === "programmatic_fallback"
|
|
1005
1068
|
? "extractive-fallback"
|
|
1006
1069
|
: "extractive"
|
|
1007
|
-
const sourceHash =
|
|
1008
|
-
const sourceCoverage =
|
|
1070
|
+
const sourceHash = preparedSummary.sourceHash
|
|
1071
|
+
const sourceCoverage = preparedSummary.sourceCoverage
|
|
1009
1072
|
created.block.autoSummaryRepresentation = summaryRepresentation
|
|
1010
1073
|
created.block.sourceHash = sourceHash
|
|
1011
1074
|
created.block.sourceCoverage = sourceCoverage
|
|
@@ -1013,11 +1076,16 @@ export async function createAutoCompressionBlock(
|
|
|
1013
1076
|
const sourceExactEstimate = messagesInRange.reduce((sum, message) => sum + estimateMessageTokens(message), 0)
|
|
1014
1077
|
const replacementExactEstimate = estimateCompressionBlockReplacementTokens(created.block)
|
|
1015
1078
|
const projectedGain = sourceExactEstimate - replacementExactEstimate
|
|
1079
|
+
const blockedSummaryDetails = {
|
|
1080
|
+
summarizerFallbackUsed: preparedSummary.representation === "extractive-fallback",
|
|
1081
|
+
summarizerAttempts: preparedSummary.summarizerAttempts,
|
|
1082
|
+
}
|
|
1016
1083
|
if (projectedGain <= 0) {
|
|
1017
1084
|
throw new AutoCompressionBlockedError(
|
|
1018
1085
|
"non-positive-gain",
|
|
1019
1086
|
`Auto-compress rejected non-positive full-projection gain for ${effectiveCandidate.startId}..${effectiveCandidate.endId}: ` +
|
|
1020
1087
|
`source ${sourceExactEstimate} tokens, replacement ${replacementExactEstimate} tokens`,
|
|
1088
|
+
blockedSummaryDetails,
|
|
1021
1089
|
)
|
|
1022
1090
|
}
|
|
1023
1091
|
const requiredGainTokens = Math.max(0, Math.floor(options.requiredGainTokens ?? 0))
|
|
@@ -1026,6 +1094,7 @@ export async function createAutoCompressionBlock(
|
|
|
1026
1094
|
"budget-exhausted",
|
|
1027
1095
|
`Auto-compress projected gain for ${effectiveCandidate.startId}..${effectiveCandidate.endId} is below required budget recovery: ` +
|
|
1028
1096
|
`${projectedGain} < ${requiredGainTokens} tokens`,
|
|
1097
|
+
blockedSummaryDetails,
|
|
1029
1098
|
)
|
|
1030
1099
|
}
|
|
1031
1100
|
|
|
@@ -1037,12 +1106,22 @@ export async function createAutoCompressionBlock(
|
|
|
1037
1106
|
fullProjectedAfterTokens = finalProjection.reduce((sum, message) => sum + estimateMessageTokens(message), 0)
|
|
1038
1107
|
fullProjectionGain = fullBefore - fullProjectedAfterTokens
|
|
1039
1108
|
if (fullProjectionGain <= 0 || (fullProjectionGain < requiredGainTokens && !options.allowPartialGain)) {
|
|
1040
|
-
throw new AutoCompressionBlockedError(
|
|
1109
|
+
throw new AutoCompressionBlockedError(
|
|
1110
|
+
"budget-exhausted",
|
|
1111
|
+
`Final provider projection saves ${fullProjectionGain}, below required ${requiredGainTokens}; no state published`,
|
|
1112
|
+
blockedSummaryDetails,
|
|
1113
|
+
)
|
|
1041
1114
|
}
|
|
1042
1115
|
}
|
|
1043
1116
|
const pressureRelieved = workingState.compressionProgress
|
|
1044
1117
|
? settleCompressionProgress(workingState, fullProjectionGain, fullProjectedAfterTokens)
|
|
1045
1118
|
: true
|
|
1119
|
+
created.block.commitMetrics = {
|
|
1120
|
+
operationId: `auto:${created.block.id}`,
|
|
1121
|
+
kind: effectiveCandidate.includedBlockIds.length >= 2 && sourceCoverage.itemCount === effectiveCandidate.includedBlockIds.length ? "consolidation" : "auto",
|
|
1122
|
+
beforeTokens: fullProjectedAfterTokens + fullProjectionGain,
|
|
1123
|
+
afterTokens: fullProjectedAfterTokens, netGainTokens: fullProjectionGain,
|
|
1124
|
+
}
|
|
1046
1125
|
assertCurrent()
|
|
1047
1126
|
let published = false
|
|
1048
1127
|
if (options.persistState) await options.persistState(workingState, {
|