pi-ui-extend 1.0.39 → 1.0.41
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 +1 -1
- package/dist/app/commands/command-registry.js +2 -2
- package/dist/app/commands/command-session-actions.d.ts +0 -1
- package/dist/app/commands/command-session-actions.js +22 -13
- package/dist/app/icons.d.ts +14 -0
- package/dist/app/icons.js +33 -0
- package/dist/app/rendering/conversation-tool-renderer.js +2 -2
- package/dist/app/rendering/dcp-stats.d.ts +6 -1
- package/dist/app/rendering/dcp-stats.js +214 -46
- package/dist/app/rendering/editor-panels.js +8 -5
- package/dist/app/session/lazy-session-manager.js +12 -1
- package/dist/app/session/tabs-controller.d.ts +2 -5
- package/dist/app/session/tabs-controller.js +12 -21
- package/dist/app/subagents/subagents-model.d.ts +14 -1
- package/dist/app/subagents/subagents-model.js +34 -15
- package/dist/app/types.d.ts +2 -0
- package/dist/bundled-extensions/session-title/config.js +1 -1
- package/dist/markdown-format.js +27 -9
- package/dist/schemas/pi-tools-suite-schema.d.ts +29 -16
- package/dist/schemas/pi-tools-suite-schema.js +46 -31
- package/external/pi-tools-suite/README.md +392 -52
- package/external/pi-tools-suite/docs/browser-qa-subagent.md +31 -21
- package/external/pi-tools-suite/docs/context-gateway-p00-adr.md +216 -0
- package/external/pi-tools-suite/docs/context-gateway-p01n-gate-review.md +122 -0
- package/external/pi-tools-suite/docs/context-gateway-p01n-measurement.md +133 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-ra-evidence.md +111 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rb-evidence.md +100 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rc-evidence.md +69 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rd-evidence.md +100 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-re-evidence.md +74 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rf-evidence.md +153 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rg-evidence.md +235 -0
- package/external/pi-tools-suite/docs/evals.md +684 -0
- package/external/pi-tools-suite/docs/subagent-model-pools.md +109 -0
- package/external/pi-tools-suite/package.json +10 -3
- package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/scripts/browser-qa-runner.mjs +82 -1
- package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/SKILL.md → agents/browser-qa.md} +261 -12
- package/external/pi-tools-suite/src/async-subagents/agents/implement.md +20 -0
- package/external/pi-tools-suite/src/async-subagents/agents/oracle.md +16 -0
- package/external/pi-tools-suite/src/async-subagents/agents/research.md +18 -0
- package/external/pi-tools-suite/src/async-subagents/agents/verify.md +18 -0
- package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +27 -243
- package/external/pi-tools-suite/src/async-subagents/commands.ts +6 -2
- package/external/pi-tools-suite/src/async-subagents/core/agent-catalog.ts +41 -0
- package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +13 -93
- package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +494 -0
- package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +9 -0
- package/external/pi-tools-suite/src/async-subagents/core/config.ts +200 -143
- package/external/pi-tools-suite/src/async-subagents/core/model-fallback.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +54 -0
- package/external/pi-tools-suite/src/async-subagents/core/prompt.ts +7 -6
- package/external/pi-tools-suite/src/async-subagents/core/routing.ts +52 -45
- package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +12 -4
- package/external/pi-tools-suite/src/async-subagents/index.ts +11 -1
- package/external/pi-tools-suite/src/async-subagents/lib.ts +6 -2
- package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +46 -18
- package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +3 -2
- package/external/pi-tools-suite/src/async-subagents/types.ts +2 -0
- package/external/pi-tools-suite/src/coding-discipline/index.ts +41 -142
- package/external/pi-tools-suite/src/config.ts +1 -22
- package/external/pi-tools-suite/src/context-gateway/accounting.ts +151 -0
- package/external/pi-tools-suite/src/context-gateway/config.ts +111 -0
- package/external/pi-tools-suite/src/context-gateway/index.ts +160 -0
- package/external/pi-tools-suite/src/context-gateway/metadata-normalization.ts +88 -0
- package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +89 -0
- package/external/pi-tools-suite/src/context-gateway/telemetry.ts +429 -0
- package/external/pi-tools-suite/src/context-gateway/test-output-parser.ts +326 -0
- package/external/pi-tools-suite/src/context-gateway/types.ts +152 -0
- package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +106 -0
- package/external/pi-tools-suite/src/dcp/auto-compress.ts +810 -106
- package/external/pi-tools-suite/src/dcp/commands.ts +64 -139
- package/external/pi-tools-suite/src/dcp/compress-tool.ts +369 -35
- package/external/pi-tools-suite/src/dcp/compression-blocks.ts +510 -64
- package/external/pi-tools-suite/src/dcp/compression-preview.ts +113 -0
- package/external/pi-tools-suite/src/dcp/compression-progress.ts +70 -0
- package/external/pi-tools-suite/src/dcp/config.ts +36 -61
- package/external/pi-tools-suite/src/dcp/conversation-index.ts +421 -0
- package/external/pi-tools-suite/src/dcp/debug-log.ts +7 -5
- package/external/pi-tools-suite/src/dcp/index.ts +617 -203
- package/external/pi-tools-suite/src/dcp/journal.ts +566 -0
- package/external/pi-tools-suite/src/dcp/progress-controller.ts +244 -0
- package/external/pi-tools-suite/src/dcp/prompts.ts +10 -7
- package/external/pi-tools-suite/src/dcp/provider-tool-results.ts +189 -0
- package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +298 -78
- package/external/pi-tools-suite/src/dcp/pruner-compression-blocks.ts +173 -281
- package/external/pi-tools-suite/src/dcp/pruner-emergency.ts +2 -4
- package/external/pi-tools-suite/src/dcp/pruner-message-ids.ts +17 -5
- package/external/pi-tools-suite/src/dcp/pruner-metadata.ts +11 -1
- package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +30 -82
- package/external/pi-tools-suite/src/dcp/pruner-tools.ts +22 -133
- package/external/pi-tools-suite/src/dcp/pruner.ts +18 -33
- package/external/pi-tools-suite/src/dcp/recovery.ts +129 -0
- package/external/pi-tools-suite/src/dcp/shadow-plan.ts +127 -0
- package/external/pi-tools-suite/src/dcp/state-transaction.ts +102 -0
- package/external/pi-tools-suite/src/dcp/state.ts +158 -580
- package/external/pi-tools-suite/src/dcp/ui.ts +1 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +55 -220
- package/external/pi-tools-suite/src/index.ts +9 -0
- package/external/pi-tools-suite/src/model-tools/index.ts +76 -42
- package/external/pi-tools-suite/src/repo-discovery/index.ts +84 -18
- package/external/pi-tools-suite/src/repo-discovery/native-compact.ts +458 -0
- package/external/pi-tools-suite/src/session-recovery/index.ts +189 -43
- package/external/pi-tools-suite/src/tool-descriptions.ts +43 -38
- package/external/pi-tools-suite/src/truncation-metadata-normalizer/index.ts +17 -0
- package/package.json +6 -6
- package/schemas/pi-tools-suite.json +159 -78
- package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/auth-scaffold-spec.md +0 -78
- package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/qa-design.md +0 -223
- package/external/pi-tools-suite/src/dcp/state-persistence.ts +0 -195
- /package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/references → agents/browser-qa/examples}/qa-auth.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/references → agents/browser-qa/examples}/qa-flow.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/vendor/fflate.LICENSE +0 -0
- /package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/vendor/fflate.mjs +0 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
export type DcpProgressPhase =
|
|
2
|
+
| "normal"
|
|
3
|
+
| "pressure"
|
|
4
|
+
| "awaiting_opportunity"
|
|
5
|
+
| "preparing"
|
|
6
|
+
| "committed"
|
|
7
|
+
| "cooldown"
|
|
8
|
+
| "blocked"
|
|
9
|
+
| "degraded";
|
|
10
|
+
|
|
11
|
+
export type DcpBlockedReason =
|
|
12
|
+
| "live-head-only"
|
|
13
|
+
| "protected-budget-exceeded"
|
|
14
|
+
| "evidence-unknown"
|
|
15
|
+
| "summarizer-unavailable"
|
|
16
|
+
| "non-positive-gain"
|
|
17
|
+
| "missing-source"
|
|
18
|
+
| "budget-exhausted";
|
|
19
|
+
|
|
20
|
+
export interface DcpBlockedReasonInput {
|
|
21
|
+
pressured: boolean;
|
|
22
|
+
candidateAvailable: boolean;
|
|
23
|
+
messageCandidateCount?: number;
|
|
24
|
+
requiredSavingsTokens?: number;
|
|
25
|
+
capacityExceeded?: boolean;
|
|
26
|
+
emergencyStats?: {
|
|
27
|
+
totalPairs: number;
|
|
28
|
+
eligiblePairs: number;
|
|
29
|
+
eligibleRecoverableTokens: number;
|
|
30
|
+
preservedRecentPairs: number;
|
|
31
|
+
preservedUnseenPairs: number;
|
|
32
|
+
preservedProtectedPairs: number;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Classify an exhausted E05 planning pass without guessing beyond the facts
|
|
38
|
+
* available from the emergency eligibility analysis. This is deliberately
|
|
39
|
+
* conservative: when a safe body candidate still exists, the controller is
|
|
40
|
+
* waiting for an opportunity rather than terminally blocked.
|
|
41
|
+
*/
|
|
42
|
+
export function inferDcpBlockedReason(input: DcpBlockedReasonInput): DcpBlockedReason | undefined {
|
|
43
|
+
if (!input.pressured || input.candidateAvailable || (input.messageCandidateCount ?? 0) > 0) return undefined;
|
|
44
|
+
|
|
45
|
+
const stats = input.emergencyStats;
|
|
46
|
+
if (!stats || stats.totalPairs === 0) return "live-head-only";
|
|
47
|
+
|
|
48
|
+
const requiredSavings = Math.max(0, Math.floor(input.requiredSavingsTokens ?? 0));
|
|
49
|
+
if (
|
|
50
|
+
stats.eligiblePairs > 0 &&
|
|
51
|
+
requiredSavings > 0 &&
|
|
52
|
+
stats.eligibleRecoverableTokens < requiredSavings
|
|
53
|
+
) {
|
|
54
|
+
return "budget-exhausted";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (stats.preservedUnseenPairs > 0 && stats.eligiblePairs === 0) return "evidence-unknown";
|
|
58
|
+
if (
|
|
59
|
+
stats.preservedProtectedPairs > 0 &&
|
|
60
|
+
(stats.eligiblePairs === 0 || input.capacityExceeded === true)
|
|
61
|
+
) {
|
|
62
|
+
return "protected-budget-exceeded";
|
|
63
|
+
}
|
|
64
|
+
if (stats.preservedRecentPairs >= stats.totalPairs && stats.eligiblePairs === 0) return "live-head-only";
|
|
65
|
+
return "budget-exhausted";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface DcpProgressInput {
|
|
69
|
+
enabled: boolean;
|
|
70
|
+
autoEnabled: boolean;
|
|
71
|
+
pressure: boolean;
|
|
72
|
+
candidateAvailable: boolean;
|
|
73
|
+
ignoredOpportunities: number;
|
|
74
|
+
patience: number;
|
|
75
|
+
preparing?: boolean;
|
|
76
|
+
justCommitted?: boolean;
|
|
77
|
+
cooldown?: boolean;
|
|
78
|
+
blockedReason?: DcpBlockedReason;
|
|
79
|
+
degradedReason?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface DcpProgressDecision {
|
|
83
|
+
phase: DcpProgressPhase;
|
|
84
|
+
shouldPrepare: boolean;
|
|
85
|
+
reason: string;
|
|
86
|
+
blockedReason?: DcpBlockedReason;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Pure E05 progress policy. Runtime hooks own opportunity accounting; this
|
|
91
|
+
* function only maps the current pressure/candidate/budget state to the next
|
|
92
|
+
* observable phase. In particular, context callback count is not an input.
|
|
93
|
+
*/
|
|
94
|
+
export function decideDcpProgress(input: DcpProgressInput): DcpProgressDecision {
|
|
95
|
+
if (!input.enabled) return { phase: "blocked", shouldPrepare: false, reason: "disabled" };
|
|
96
|
+
if (input.justCommitted) return { phase: "committed", shouldPrepare: false, reason: "committed" };
|
|
97
|
+
if (input.cooldown) return { phase: "cooldown", shouldPrepare: false, reason: "cooldown" };
|
|
98
|
+
if (input.degradedReason) return { phase: "degraded", shouldPrepare: false, reason: input.degradedReason };
|
|
99
|
+
if (input.blockedReason) {
|
|
100
|
+
return {
|
|
101
|
+
phase: "blocked",
|
|
102
|
+
shouldPrepare: false,
|
|
103
|
+
reason: input.blockedReason,
|
|
104
|
+
blockedReason: input.blockedReason,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (!input.pressure) return { phase: "normal", shouldPrepare: false, reason: "below-pressure" };
|
|
108
|
+
if (input.preparing) return { phase: "preparing", shouldPrepare: false, reason: "already-preparing" };
|
|
109
|
+
if (!input.autoEnabled) return { phase: "pressure", shouldPrepare: false, reason: "auto-disabled" };
|
|
110
|
+
if (!input.candidateAvailable) {
|
|
111
|
+
return { phase: "awaiting_opportunity", shouldPrepare: false, reason: "no-candidate" };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const ignored = Math.max(0, Math.floor(input.ignoredOpportunities));
|
|
115
|
+
const patience = Math.max(0, Math.floor(input.patience));
|
|
116
|
+
if (ignored <= patience) {
|
|
117
|
+
return { phase: "awaiting_opportunity", shouldPrepare: false, reason: "below-patience" };
|
|
118
|
+
}
|
|
119
|
+
return { phase: "preparing", shouldPrepare: true, reason: "ignored-opportunities" };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export type DcpBudgetProjectionOrigin = "repo-fallback" | "provider-native" | "repo-over-provider";
|
|
123
|
+
|
|
124
|
+
export interface DcpBudgetInput {
|
|
125
|
+
providerUsageTokens?: number | null;
|
|
126
|
+
repoProjectedTokens: number;
|
|
127
|
+
contextWindow: number;
|
|
128
|
+
reservedOutputTokens?: number;
|
|
129
|
+
reservedToolTokens?: number;
|
|
130
|
+
maxContextPercent: number;
|
|
131
|
+
hardContextPercent: number;
|
|
132
|
+
targetContextPercent?: number;
|
|
133
|
+
summaryBufferEnabled?: boolean;
|
|
134
|
+
activeSummaryTokens?: number;
|
|
135
|
+
summaryBufferMaxBonusRatio?: number;
|
|
136
|
+
estimatorMarginTokens?: number;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface DcpBudgetPlan {
|
|
140
|
+
projectedBeforeTokens: number;
|
|
141
|
+
projectionOrigin: DcpBudgetProjectionOrigin;
|
|
142
|
+
contextWindow: number;
|
|
143
|
+
reservedOutputTokens: number;
|
|
144
|
+
reservedToolTokens: number;
|
|
145
|
+
inputCapacityTokens: number;
|
|
146
|
+
summaryBufferTokensApplied: number;
|
|
147
|
+
softHeadroomTokens: number;
|
|
148
|
+
hardHeadroomTokens: number;
|
|
149
|
+
targetHeadroomTokens: number;
|
|
150
|
+
requiredSavingsTokens: number;
|
|
151
|
+
pressured: boolean;
|
|
152
|
+
hardPressure: boolean;
|
|
153
|
+
capacityExceeded: boolean;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function finiteNonNegative(value: unknown): number | undefined {
|
|
157
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function clampFraction(value: number): number {
|
|
161
|
+
return Math.max(0, Math.min(1, Number.isFinite(value) ? value : 0));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Pure E05 budget planner. Provider-native usage is useful evidence but may be
|
|
166
|
+
* stale between completed requests, so the fresh repo projection is always a
|
|
167
|
+
* lower bound. Capacity is distinct from policy thresholds and explicitly
|
|
168
|
+
* reserves output/tool budget. `summaryBuffer` may relax the soft policy only
|
|
169
|
+
* inside that hard capacity.
|
|
170
|
+
*/
|
|
171
|
+
export function planDcpBudget(input: DcpBudgetInput): DcpBudgetPlan {
|
|
172
|
+
const contextWindow = Math.max(1, Math.floor(finiteNonNegative(input.contextWindow) ?? 1));
|
|
173
|
+
const repoProjectedTokens = Math.floor(finiteNonNegative(input.repoProjectedTokens) ?? 0);
|
|
174
|
+
const providerUsageTokens = finiteNonNegative(input.providerUsageTokens);
|
|
175
|
+
const projectedBeforeTokens = Math.max(repoProjectedTokens, Math.floor(providerUsageTokens ?? 0));
|
|
176
|
+
const projectionOrigin: DcpBudgetProjectionOrigin = providerUsageTokens === undefined
|
|
177
|
+
? "repo-fallback"
|
|
178
|
+
: repoProjectedTokens > providerUsageTokens
|
|
179
|
+
? "repo-over-provider"
|
|
180
|
+
: "provider-native";
|
|
181
|
+
|
|
182
|
+
const reservedOutputTokens = Math.min(
|
|
183
|
+
contextWindow,
|
|
184
|
+
Math.floor(finiteNonNegative(input.reservedOutputTokens) ?? 0),
|
|
185
|
+
);
|
|
186
|
+
const reservedToolTokens = Math.min(
|
|
187
|
+
Math.max(0, contextWindow - reservedOutputTokens),
|
|
188
|
+
Math.floor(finiteNonNegative(input.reservedToolTokens) ?? 0),
|
|
189
|
+
);
|
|
190
|
+
const inputCapacityTokens = Math.max(0, contextWindow - reservedOutputTokens - reservedToolTokens);
|
|
191
|
+
|
|
192
|
+
const baseSoftHeadroom = Math.min(
|
|
193
|
+
inputCapacityTokens,
|
|
194
|
+
Math.floor(clampFraction(input.maxContextPercent) * contextWindow),
|
|
195
|
+
);
|
|
196
|
+
const summaryBufferCap = Math.floor(
|
|
197
|
+
clampFraction(input.summaryBufferMaxBonusRatio ?? 0.05) * contextWindow,
|
|
198
|
+
);
|
|
199
|
+
const requestedSummaryBuffer = input.summaryBufferEnabled
|
|
200
|
+
? Math.floor(finiteNonNegative(input.activeSummaryTokens) ?? 0)
|
|
201
|
+
: 0;
|
|
202
|
+
const summaryBufferTokensApplied = Math.min(
|
|
203
|
+
requestedSummaryBuffer,
|
|
204
|
+
summaryBufferCap,
|
|
205
|
+
Math.max(0, inputCapacityTokens - baseSoftHeadroom),
|
|
206
|
+
);
|
|
207
|
+
const softHeadroomTokens = baseSoftHeadroom + summaryBufferTokensApplied;
|
|
208
|
+
const hardHeadroomTokens = Math.min(
|
|
209
|
+
inputCapacityTokens,
|
|
210
|
+
Math.floor(clampFraction(input.hardContextPercent) * contextWindow),
|
|
211
|
+
);
|
|
212
|
+
const capacityExceeded = projectedBeforeTokens > inputCapacityTokens;
|
|
213
|
+
const hardPressure = capacityExceeded || projectedBeforeTokens > hardHeadroomTokens;
|
|
214
|
+
const pressured = hardPressure || projectedBeforeTokens > softHeadroomTokens;
|
|
215
|
+
|
|
216
|
+
const configuredTarget = clampFraction(input.targetContextPercent ?? input.maxContextPercent);
|
|
217
|
+
const recoveryHeadroom = Math.min(
|
|
218
|
+
inputCapacityTokens,
|
|
219
|
+
softHeadroomTokens,
|
|
220
|
+
Math.floor(configuredTarget * contextWindow),
|
|
221
|
+
);
|
|
222
|
+
const targetHeadroomTokens = hardPressure ? recoveryHeadroom : softHeadroomTokens;
|
|
223
|
+
const estimatorMarginTokens = Math.floor(finiteNonNegative(input.estimatorMarginTokens) ?? 0);
|
|
224
|
+
const requiredSavingsTokens = pressured
|
|
225
|
+
? Math.max(0, projectedBeforeTokens - targetHeadroomTokens + estimatorMarginTokens)
|
|
226
|
+
: 0;
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
projectedBeforeTokens,
|
|
230
|
+
projectionOrigin,
|
|
231
|
+
contextWindow,
|
|
232
|
+
reservedOutputTokens,
|
|
233
|
+
reservedToolTokens,
|
|
234
|
+
inputCapacityTokens,
|
|
235
|
+
summaryBufferTokensApplied,
|
|
236
|
+
softHeadroomTokens,
|
|
237
|
+
hardHeadroomTokens,
|
|
238
|
+
targetHeadroomTokens,
|
|
239
|
+
requiredSavingsTokens,
|
|
240
|
+
pressured,
|
|
241
|
+
hardPressure,
|
|
242
|
+
capacityExceeded,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
@@ -52,6 +52,9 @@ Passing check/test/lint/tsc logs are summary-only after you know the result. Pre
|
|
|
52
52
|
DCP REMINDERS
|
|
53
53
|
If a \`<dcp-system-reminder>\` is present in context, treat it as a signal to evaluate compression. Critical or high-context reminders should be handled promptly. Routine reminders should lead to compression only when a safe, closed, useful-to-summarize range exists; otherwise continue the next atomic step and re-check later.
|
|
54
54
|
|
|
55
|
+
COMMITTED IS NOT NECESSARILY RECOVERED
|
|
56
|
+
The tool rejects non-positive full-projection gain, including wrappers and preserved content. An unsuccessful call does not satisfy the reminder. Inspect \`netGain\`, \`pressureRelieved\`, and \`remainingRecoveryTokens\` in its result: a partial positive commit may still leave context pressure. Use freshly visible IDs for subsequent work; do not retry an unchanged rejected summary or discard required facts just to meet a token goal.
|
|
57
|
+
|
|
55
58
|
THE SUMMARY
|
|
56
59
|
Your summary must be COMPLETE FOR CONTINUATION, not a transcript rewrite. Preserve only information that will plausibly matter later: user intent, accepted constraints, decisions, files/symbols changed or inspected, exact errors that are still actionable, verification status, and next steps.
|
|
57
60
|
|
|
@@ -84,7 +87,9 @@ PROTECTED PROMPT CONTENT
|
|
|
84
87
|
If selected user text contains \`<protect>...</protect>\` content, preserve that protected content verbatim in your summary. The tool may append protected text automatically, but you should still account for it semantically.
|
|
85
88
|
|
|
86
89
|
COMPRESSED BLOCK PLACEHOLDERS
|
|
87
|
-
When the
|
|
90
|
+
When rolling up compressed blocks, summarize their continuation-relevant meaning instead of copying the full old summaries. Every block carries a protected-fragment ledger, and those fragments are preserved separately. A block missing that ledger is invalid and must not be rolled up.
|
|
91
|
+
|
|
92
|
+
Only when the full previous summary is genuinely needed verbatim, use this exact placeholder format:
|
|
88
93
|
|
|
89
94
|
- \`(bN)\`
|
|
90
95
|
|
|
@@ -96,11 +101,11 @@ Compressed block IDs always use the \`bN\` form (never \`mNNN\`) and are represe
|
|
|
96
101
|
|
|
97
102
|
Rules:
|
|
98
103
|
|
|
99
|
-
-
|
|
104
|
+
- Placeholders are optional, not required for every covered modern block. Each explicit placeholder expands the full old summary and may eliminate the intended savings. Include an intentionally preserved block at most once.
|
|
100
105
|
- Do not invent placeholders for blocks outside the selected range.
|
|
101
106
|
- Treat \`(bN)\` placeholders as RESERVED TOKENS. Do not emit \`(bN)\` text anywhere except intentional placeholders.
|
|
102
107
|
- If you need to mention a block in prose, use plain text like \`compressed bN\` (not as a placeholder).
|
|
103
|
-
- Preflight check before finalizing:
|
|
108
|
+
- Preflight check before finalizing: each placeholder must refer to a known selected block whose full text you deliberately need to preserve. Otherwise write a concise factual summary in your own words.
|
|
104
109
|
|
|
105
110
|
These placeholders are semantic references. They will be replaced with the full stored compressed block content when the tool processes your output.
|
|
106
111
|
|
|
@@ -125,10 +130,8 @@ Treat these reference lines as boundary metadata only, not as tool result conten
|
|
|
125
130
|
|
|
126
131
|
Rules:
|
|
127
132
|
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
- \`startId\` must appear before \`endId\`.
|
|
131
|
-
- Do not invent IDs. Use only IDs that are present in context.
|
|
133
|
+
- Use only injected IDs in current context; \`startId\` must precede \`endId\`. Do not invent IDs.
|
|
134
|
+
- For \`ranges\`, never split a tool group: include the calling assistant and all its tool results, including parallel calls.
|
|
132
135
|
- If \`compress\` fails with \`Unknown message ID\`, treat those IDs as stale; retry at most once using only the error's listed current raw IDs or eligible \`bN\` blocks, or skip if no closed range is safe.
|
|
133
136
|
|
|
134
137
|
BATCHING
|
|
@@ -84,3 +84,192 @@ export function providerPayloadIncludesToolResult(
|
|
|
84
84
|
return typeof record.outputText === "string" &&
|
|
85
85
|
evidence.anonymousSignatures.has(signature(record.toolName, record.outputText));
|
|
86
86
|
}
|
|
87
|
+
|
|
88
|
+
export function providerPayloadRevision(payload: unknown): string | undefined {
|
|
89
|
+
try {
|
|
90
|
+
const serialized = JSON.stringify(payload);
|
|
91
|
+
if (serialized === undefined) return undefined;
|
|
92
|
+
return createHash("sha256").update(serialized).digest("hex");
|
|
93
|
+
} catch {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Verify delivery of the exact trusted reminder, not just a context callback. */
|
|
99
|
+
export function providerPayloadIncludesReminder(payload: unknown, reminder: string | undefined): boolean {
|
|
100
|
+
if (!reminder) return false;
|
|
101
|
+
try {
|
|
102
|
+
const serialized = JSON.stringify(payload);
|
|
103
|
+
return typeof serialized === "string" && serialized.includes(JSON.stringify(reminder).slice(1, -1));
|
|
104
|
+
} catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface ProviderEvidenceAttempt {
|
|
110
|
+
sessionEpoch: number;
|
|
111
|
+
provider?: string;
|
|
112
|
+
model?: string;
|
|
113
|
+
contentRevision?: string;
|
|
114
|
+
statePath?: string;
|
|
115
|
+
sessionId?: string;
|
|
116
|
+
toolIds: ReadonlySet<string>;
|
|
117
|
+
opportunityAvailable?: boolean;
|
|
118
|
+
opportunityKind?: "routine" | "emergency";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface ProviderEvidenceFinalMessage {
|
|
122
|
+
sessionEpoch: number;
|
|
123
|
+
provider?: string;
|
|
124
|
+
model?: string;
|
|
125
|
+
stopReason?: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface ProviderEvidencePendingSnapshot {
|
|
129
|
+
firstAttemptId: number;
|
|
130
|
+
lastAttemptId: number;
|
|
131
|
+
attempts: number;
|
|
132
|
+
sessionEpoch: number;
|
|
133
|
+
provider?: string;
|
|
134
|
+
model?: string;
|
|
135
|
+
contentRevision?: string;
|
|
136
|
+
statePath?: string;
|
|
137
|
+
sessionId?: string;
|
|
138
|
+
toolIds: Set<string>;
|
|
139
|
+
opportunityAvailable: boolean;
|
|
140
|
+
opportunityKind?: "routine" | "emergency";
|
|
141
|
+
ambiguous: boolean;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export type ProviderEvidenceCompletion =
|
|
145
|
+
| {
|
|
146
|
+
status: "promote";
|
|
147
|
+
firstAttemptId: number;
|
|
148
|
+
lastAttemptId: number;
|
|
149
|
+
attempts: number;
|
|
150
|
+
sessionEpoch: number;
|
|
151
|
+
statePath?: string;
|
|
152
|
+
sessionId?: string;
|
|
153
|
+
toolIds: Set<string>;
|
|
154
|
+
opportunityAvailable: boolean;
|
|
155
|
+
opportunityKind?: "routine" | "emergency";
|
|
156
|
+
}
|
|
157
|
+
| {
|
|
158
|
+
status: "refused";
|
|
159
|
+
reason:
|
|
160
|
+
| "no-pending"
|
|
161
|
+
| "terminal-failure"
|
|
162
|
+
| "ambiguous"
|
|
163
|
+
| "missing-identity"
|
|
164
|
+
| "stale-session"
|
|
165
|
+
| "identity-mismatch";
|
|
166
|
+
attempts: number;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
function sameStringSet(left: ReadonlySet<string>, right: ReadonlySet<string>): boolean {
|
|
170
|
+
if (left.size !== right.size) return false;
|
|
171
|
+
for (const value of left) {
|
|
172
|
+
if (!right.has(value)) return false;
|
|
173
|
+
}
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function attemptHasIdentity(attempt: ProviderEvidenceAttempt): boolean {
|
|
178
|
+
return Boolean(attempt.provider && attempt.model && attempt.contentRevision);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Correlates provider-input evidence with one finalized assistant message without
|
|
183
|
+
* inventing a request ID the extension API does not expose. Repeated identical
|
|
184
|
+
* attempts are treated as retries; any changed model/content/owner while an
|
|
185
|
+
* attempt is pending makes the evidence ambiguous and therefore non-promotable.
|
|
186
|
+
*/
|
|
187
|
+
export class ProviderEvidenceTracker {
|
|
188
|
+
private pending: ProviderEvidencePendingSnapshot | undefined;
|
|
189
|
+
private nextAttemptId = 1;
|
|
190
|
+
|
|
191
|
+
begin(attempt: ProviderEvidenceAttempt): ProviderEvidencePendingSnapshot {
|
|
192
|
+
const attemptId = this.nextAttemptId++;
|
|
193
|
+
const toolIds = new Set(attempt.toolIds);
|
|
194
|
+
if (!this.pending) {
|
|
195
|
+
this.pending = {
|
|
196
|
+
firstAttemptId: attemptId,
|
|
197
|
+
lastAttemptId: attemptId,
|
|
198
|
+
attempts: 1,
|
|
199
|
+
sessionEpoch: attempt.sessionEpoch,
|
|
200
|
+
provider: attempt.provider,
|
|
201
|
+
model: attempt.model,
|
|
202
|
+
contentRevision: attempt.contentRevision,
|
|
203
|
+
statePath: attempt.statePath,
|
|
204
|
+
sessionId: attempt.sessionId,
|
|
205
|
+
toolIds,
|
|
206
|
+
opportunityAvailable: Boolean(attempt.opportunityAvailable),
|
|
207
|
+
opportunityKind: attempt.opportunityKind,
|
|
208
|
+
ambiguous: !attemptHasIdentity(attempt),
|
|
209
|
+
};
|
|
210
|
+
return this.snapshot()!;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const pending = this.pending;
|
|
214
|
+
const sameAttemptShape =
|
|
215
|
+
pending.sessionEpoch === attempt.sessionEpoch &&
|
|
216
|
+
pending.provider === attempt.provider &&
|
|
217
|
+
pending.model === attempt.model &&
|
|
218
|
+
pending.contentRevision === attempt.contentRevision &&
|
|
219
|
+
pending.statePath === attempt.statePath &&
|
|
220
|
+
pending.sessionId === attempt.sessionId &&
|
|
221
|
+
pending.opportunityAvailable === Boolean(attempt.opportunityAvailable) &&
|
|
222
|
+
pending.opportunityKind === attempt.opportunityKind &&
|
|
223
|
+
sameStringSet(pending.toolIds, toolIds);
|
|
224
|
+
|
|
225
|
+
pending.lastAttemptId = attemptId;
|
|
226
|
+
pending.attempts++;
|
|
227
|
+
if (!sameAttemptShape || !attemptHasIdentity(attempt)) pending.ambiguous = true;
|
|
228
|
+
return this.snapshot()!;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
complete(finalMessage: ProviderEvidenceFinalMessage): ProviderEvidenceCompletion {
|
|
232
|
+
const pending = this.pending;
|
|
233
|
+
this.pending = undefined;
|
|
234
|
+
if (!pending) return { status: "refused", reason: "no-pending", attempts: 0 };
|
|
235
|
+
|
|
236
|
+
const successfulStopReasons = new Set(["stop", "length", "toolUse", "deferred"]);
|
|
237
|
+
if (!finalMessage.stopReason || !successfulStopReasons.has(finalMessage.stopReason)) {
|
|
238
|
+
return { status: "refused", reason: "terminal-failure", attempts: pending.attempts };
|
|
239
|
+
}
|
|
240
|
+
if (pending.ambiguous) {
|
|
241
|
+
return { status: "refused", reason: "ambiguous", attempts: pending.attempts };
|
|
242
|
+
}
|
|
243
|
+
if (!pending.provider || !pending.model || !pending.contentRevision || !finalMessage.provider || !finalMessage.model) {
|
|
244
|
+
return { status: "refused", reason: "missing-identity", attempts: pending.attempts };
|
|
245
|
+
}
|
|
246
|
+
if (pending.sessionEpoch !== finalMessage.sessionEpoch) {
|
|
247
|
+
return { status: "refused", reason: "stale-session", attempts: pending.attempts };
|
|
248
|
+
}
|
|
249
|
+
if (pending.provider !== finalMessage.provider || pending.model !== finalMessage.model) {
|
|
250
|
+
return { status: "refused", reason: "identity-mismatch", attempts: pending.attempts };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
status: "promote",
|
|
255
|
+
firstAttemptId: pending.firstAttemptId,
|
|
256
|
+
lastAttemptId: pending.lastAttemptId,
|
|
257
|
+
attempts: pending.attempts,
|
|
258
|
+
sessionEpoch: pending.sessionEpoch,
|
|
259
|
+
statePath: pending.statePath,
|
|
260
|
+
sessionId: pending.sessionId,
|
|
261
|
+
toolIds: new Set(pending.toolIds),
|
|
262
|
+
opportunityAvailable: pending.opportunityAvailable,
|
|
263
|
+
opportunityKind: pending.opportunityKind,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
snapshot(): ProviderEvidencePendingSnapshot | undefined {
|
|
268
|
+
if (!this.pending) return undefined;
|
|
269
|
+
return { ...this.pending, toolIds: new Set(this.pending.toolIds) };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
reset(): void {
|
|
273
|
+
this.pending = undefined;
|
|
274
|
+
}
|
|
275
|
+
}
|