ds4-context-engine 0.3.0-alpha.5 → 0.3.0-beta.1
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 +23 -10
- package/docs/ADR/058-bounded-manifest-storage.md +31 -0
- package/docs/ADR/README.md +1 -0
- package/docs/ARCHITECTURE.md +9 -5
- package/docs/COMPACTION.md +3 -3
- package/docs/CONTEXT_MANIFEST.md +10 -1
- package/docs/CONTEXT_PERSISTENCE_TOOL.md +2 -2
- package/docs/DOGFOODING_0.3.0_ALPHA.md +34 -9
- package/docs/DOGFOODING_0.3.0_BETA.md +331 -0
- package/docs/RELEASE_READINESS_0.2.0.md +1 -1
- package/docs/RELEASING.md +5 -5
- package/docs/STORAGE.md +16 -2
- package/docs/STORAGE_MAINTENANCE.md +121 -0
- package/docs/releases/0.3.0-alpha.5.md +5 -3
- package/docs/releases/0.3.0-beta.1.md +81 -0
- package/package.json +6 -2
- package/scripts/ds4-context-storage.mjs +148 -0
- package/src/extension/commands.ts +79 -2
- package/src/extension/runtime.ts +81 -24
- package/src/pi-adapter/compaction-coordinator.ts +12 -0
- package/src/pi-adapter/summary-generator.ts +120 -34
- package/src/pi-adapter/version.ts +1 -1
|
@@ -12,6 +12,17 @@ import {
|
|
|
12
12
|
type SummaryValidationResult,
|
|
13
13
|
} from "ds4-context-core/compaction/summary-contract";
|
|
14
14
|
|
|
15
|
+
export const COMPACTION_TRANSPORT_MAX_ATTEMPTS = 3;
|
|
16
|
+
const COMPACTION_TRANSPORT_RETRY_DELAYS_MS = [200, 500] as const;
|
|
17
|
+
|
|
18
|
+
export interface CompactionTransportRetryDiagnostic {
|
|
19
|
+
stage: "segment" | "aggregate";
|
|
20
|
+
failedAttempt: number;
|
|
21
|
+
nextAttempt: number;
|
|
22
|
+
maxAttempts: number;
|
|
23
|
+
delayMs: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
export interface GenerateValidatedSummaryInput {
|
|
16
27
|
stage: "segment" | "aggregate";
|
|
17
28
|
prompt: string;
|
|
@@ -23,6 +34,7 @@ export interface GenerateValidatedSummaryInput {
|
|
|
23
34
|
event: SessionBeforeCompactEvent;
|
|
24
35
|
ctx: ExtensionContext;
|
|
25
36
|
now: () => number;
|
|
37
|
+
onTransportRetry?: (diagnostic: CompactionTransportRetryDiagnostic) => void;
|
|
26
38
|
}
|
|
27
39
|
|
|
28
40
|
export interface GeneratedSummary {
|
|
@@ -55,60 +67,134 @@ function responseErrorMessage(response: unknown): string | undefined {
|
|
|
55
67
|
return typeof response.errorMessage === "string" ? response.errorMessage : undefined;
|
|
56
68
|
}
|
|
57
69
|
|
|
58
|
-
|
|
70
|
+
type ProviderFailureCategory =
|
|
71
|
+
| "aborted"
|
|
72
|
+
| "usage-limit"
|
|
73
|
+
| "rate-limit"
|
|
74
|
+
| "input-limit"
|
|
75
|
+
| "authentication"
|
|
76
|
+
| "transport"
|
|
77
|
+
| "provider-error";
|
|
78
|
+
|
|
79
|
+
function providerFailureCategory(value: unknown): ProviderFailureCategory {
|
|
59
80
|
const message = value instanceof Error
|
|
60
81
|
? value.message
|
|
61
82
|
: typeof value === "string"
|
|
62
83
|
? value
|
|
63
84
|
: "";
|
|
64
|
-
if (
|
|
85
|
+
if (value instanceof Error && value.name === "AbortError") return "aborted";
|
|
65
86
|
if (/usage|quota|credit|billing/iu.test(message)) return "usage-limit";
|
|
66
87
|
if (/rate|too many requests|429/iu.test(message)) return "rate-limit";
|
|
67
88
|
if (/(?:context|prompt|input).{0,48}(?:exceed|limit|maximum|too (?:long|large)|tokens?)|tokens?.{0,48}(?:exceed|limit|maximum|too many)|maximum (?:context|input|prompt|length)/iu.test(message)) {
|
|
68
89
|
return "input-limit";
|
|
69
90
|
}
|
|
70
91
|
if (/auth|credential|api.?key|permission|forbidden|401|403/iu.test(message)) return "authentication";
|
|
71
|
-
if (/timeout|network|connection|socket|dns/iu.test(message))
|
|
92
|
+
if (/timeout|timed out|network|connection|socket|dns|fetch failed|econn(?:reset|refused|aborted)|etimedout|eai_again|enotfound|und_err/iu.test(message)) {
|
|
93
|
+
return "transport";
|
|
94
|
+
}
|
|
95
|
+
if (/abort|cancel/iu.test(message)) return "aborted";
|
|
72
96
|
return "provider-error";
|
|
73
97
|
}
|
|
74
98
|
|
|
99
|
+
function abortedError(): Error {
|
|
100
|
+
return new Error("Compaction summary generation aborted");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function waitForTransportRetry(signal: AbortSignal, milliseconds: number): Promise<void> {
|
|
104
|
+
if (signal.aborted) throw abortedError();
|
|
105
|
+
await new Promise<void>((resolve, reject) => {
|
|
106
|
+
const onAbort = (): void => {
|
|
107
|
+
clearTimeout(timer);
|
|
108
|
+
reject(abortedError());
|
|
109
|
+
};
|
|
110
|
+
const timer = setTimeout(() => {
|
|
111
|
+
signal.removeEventListener("abort", onAbort);
|
|
112
|
+
resolve();
|
|
113
|
+
}, milliseconds);
|
|
114
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function transportFailureSuffix(category: ProviderFailureCategory, attempts: number): string {
|
|
119
|
+
return category === "transport"
|
|
120
|
+
? `category=${category}; attempts=${attempts}`
|
|
121
|
+
: `category=${category}`;
|
|
122
|
+
}
|
|
123
|
+
|
|
75
124
|
export async function generateValidatedSummary(
|
|
76
125
|
input: GenerateValidatedSummaryInput,
|
|
77
126
|
): Promise<GeneratedSummary> {
|
|
78
127
|
const model = input.ctx.model;
|
|
79
128
|
if (!model) throw new Error("Compaction summary generation requires an active model");
|
|
80
129
|
const maxTokens = Math.max(1, Math.min(input.maxSummaryTokens, model.maxTokens ?? input.maxSummaryTokens));
|
|
130
|
+
const retryUsages: Usage[] = [];
|
|
81
131
|
let response: Awaited<ReturnType<typeof input.ctx.modelRegistry.complete>>;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
132
|
+
let attempt = 0;
|
|
133
|
+
for (;;) {
|
|
134
|
+
if (input.event.signal.aborted) throw abortedError();
|
|
135
|
+
attempt++;
|
|
136
|
+
try {
|
|
137
|
+
response = await input.ctx.modelRegistry.complete(
|
|
138
|
+
model,
|
|
139
|
+
{
|
|
140
|
+
messages: [{
|
|
141
|
+
role: "user",
|
|
142
|
+
content: [{ type: "text", text: input.prompt }],
|
|
143
|
+
timestamp: input.now(),
|
|
144
|
+
}],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
maxTokens,
|
|
148
|
+
signal: input.event.signal,
|
|
149
|
+
cacheRetention: "none",
|
|
150
|
+
sessionId: randomUUID(),
|
|
151
|
+
},
|
|
152
|
+
);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
if (input.event.signal.aborted) throw abortedError();
|
|
155
|
+
const category = providerFailureCategory(error);
|
|
156
|
+
if (category !== "transport" || attempt >= COMPACTION_TRANSPORT_MAX_ATTEMPTS) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
`Compaction ${input.stage} request failed (${transportFailureSuffix(category, attempt)})`,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
const delayMs = COMPACTION_TRANSPORT_RETRY_DELAYS_MS[attempt - 1] ?? 500;
|
|
162
|
+
await waitForTransportRetry(input.event.signal, delayMs);
|
|
163
|
+
input.onTransportRetry?.({
|
|
164
|
+
stage: input.stage,
|
|
165
|
+
failedAttempt: attempt,
|
|
166
|
+
nextAttempt: attempt + 1,
|
|
167
|
+
maxAttempts: COMPACTION_TRANSPORT_MAX_ATTEMPTS,
|
|
168
|
+
delayMs,
|
|
169
|
+
});
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (input.event.signal.aborted) throw abortedError();
|
|
173
|
+
const stopReason = responseStopReason(response);
|
|
174
|
+
if (stopReason === "error") {
|
|
175
|
+
const category = providerFailureCategory(responseErrorMessage(response));
|
|
176
|
+
if (category === "transport" && attempt < COMPACTION_TRANSPORT_MAX_ATTEMPTS) {
|
|
177
|
+
retryUsages.push(response.usage);
|
|
178
|
+
const delayMs = COMPACTION_TRANSPORT_RETRY_DELAYS_MS[attempt - 1] ?? 500;
|
|
179
|
+
await waitForTransportRetry(input.event.signal, delayMs);
|
|
180
|
+
input.onTransportRetry?.({
|
|
181
|
+
stage: input.stage,
|
|
182
|
+
failedAttempt: attempt,
|
|
183
|
+
nextAttempt: attempt + 1,
|
|
184
|
+
maxAttempts: COMPACTION_TRANSPORT_MAX_ATTEMPTS,
|
|
185
|
+
delayMs,
|
|
186
|
+
});
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
throw new Error(
|
|
190
|
+
`Compaction ${input.stage} summary stopped with error (${transportFailureSuffix(category, attempt)})`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
if (stopReason === "aborted") {
|
|
194
|
+
throw new Error(`Compaction ${input.stage} summary stopped with aborted (category=aborted)`);
|
|
195
|
+
}
|
|
196
|
+
if (stopReason === "length") throw new Error("Compaction summary hit the model output limit");
|
|
197
|
+
break;
|
|
112
198
|
}
|
|
113
199
|
if (response.content.some((block) => block.type === "toolCall")) {
|
|
114
200
|
throw new Error("Compaction summarizer attempted to call a tool");
|
|
@@ -172,7 +258,7 @@ export async function generateValidatedSummary(
|
|
|
172
258
|
: "";
|
|
173
259
|
throw new Error(`Compaction ${input.stage} summary validation failed: ${codes}${repairDiagnostics}`);
|
|
174
260
|
}
|
|
175
|
-
return { content, validation, usage: response.usage };
|
|
261
|
+
return { content, validation, usage: sumUsage([...retryUsages, response.usage]) };
|
|
176
262
|
}
|
|
177
263
|
|
|
178
264
|
export function sumUsage(usages: readonly Usage[]): Usage {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const EXTENSION_VERSION = "0.3.0-
|
|
1
|
+
export const EXTENSION_VERSION = "0.3.0-beta.1";
|
|
2
2
|
export const SUPPORTED_PI_VERSION = "0.84.3";
|
|
3
3
|
export const OBSERVER_PLANNER_VERSION = "observer-model-aware-v1";
|
|
4
4
|
export const PLANNER_VERSION = "managed-learned-ranking-v1";
|