ds4-context-engine 0.3.2 → 0.3.3
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/docs/COMPACTION.md
CHANGED
|
@@ -13,7 +13,7 @@ DS4 intercepts Pi's `session_before_compact` event but preserves Pi's cut-point
|
|
|
13
13
|
7. The highest input classification is wrapped around each generated node, then all nodes are persisted atomically as one `prepared` graph batch. Usage is summed across every segment and aggregate request; Pi receives only the final root text and still appends exactly one canonical `CompactionEntry` with `fromHook: true`.
|
|
14
14
|
8. `session_compact` commits all nodes and associates the active root with the Pi entry; failure marks the complete prepared batch `failed`.
|
|
15
15
|
|
|
16
|
-
Fan-out and fan-in are bounded to 32 segment requests, 64 aggregate requests, and 16 aggregate passes. Transport replay
|
|
16
|
+
Fan-out and fan-in are bounded to 32 segment requests, 64 aggregate requests, and 16 aggregate passes. Transport replay mirrors Pi's assistant retry policy: `compaction.transport.maxAttempts` (default 3) total attempts and `compaction.transport.baseDelayMs` (default 2000 ms, capped at 60 s) backoff, doubling per attempt, abort-aware. Replay never applies to input, usage, rate, authentication, validation, or output-limit failures. A base prompt, individual message, atomic tool exchange, pair of child summaries, or total operation that cannot fit within those limits fails closed. Any mapping, budget, model, output-limit, validation, abort, or storage error returns `undefined` from the hook, allowing Pi's default compaction to run.
|
|
17
17
|
|
|
18
18
|
## Required summary contract
|
|
19
19
|
|
|
@@ -78,6 +78,26 @@ Semantics:
|
|
|
78
78
|
- `compaction.summary.thinking` defaults to `off` and applies only to summary requests: `off` keeps the pre-existing request shape (no thinking fields), while other levels map per API (`thinkingEnabled`/`effort` for `anthropic-messages`, `samplingParams.reasoning_effort` for OpenAI-compatible APIs) and are ignored for unsupported providers;
|
|
79
79
|
- `context.maxSummaryTokens` remains a session-level limit and does not rise for the dedicated model; the minimum with the model's `maxTokens` still applies.
|
|
80
80
|
|
|
81
|
+
## Transport retry policy
|
|
82
|
+
|
|
83
|
+
Summary requests are replayed only for transport-classified failures (thrown transport errors or `stopReason: "error"` responses whose message matches network/timeout patterns). The replay policy defaults to Pi's assistant retry policy and can be tuned per deployment:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"compaction": {
|
|
88
|
+
"transport": {
|
|
89
|
+
"maxAttempts": 3,
|
|
90
|
+
"baseDelayMs": 2000
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- `compaction.transport.maxAttempts`: total attempts per segment or aggregate call, integer 1–10, default 3. With 1, no transport failure is retried.
|
|
97
|
+
- `compaction.transport.baseDelayMs`: base backoff before the first replay, integer 0–60000, default 2000. The delay doubles per attempt (2000, 4000, 8000, …) and is capped at 60 s.
|
|
98
|
+
- Replays use a fresh routing session per attempt; diagnostics expose only stage, failed/next attempt, max attempts, and delay.
|
|
99
|
+
- Aborts (including during backoff) never trigger replay; non-transport failures are never retried; usage is summed across replayed responses.
|
|
100
|
+
|
|
81
101
|
## Proactive trigger
|
|
82
102
|
|
|
83
103
|
After a settled turn, DS4 computes:
|
package/docs/releases/0.3.2.md
CHANGED
|
@@ -65,9 +65,9 @@ Exact post-publication verification passed for all three `0.3.2` packages on 202
|
|
|
65
65
|
Published package shasums:
|
|
66
66
|
|
|
67
67
|
```text
|
|
68
|
-
ds4-context-core:
|
|
69
|
-
ds4-context-reference-adapter:
|
|
70
|
-
ds4-context-engine:
|
|
68
|
+
ds4-context-core: ff2501ffc1c12713591d97989e708733b7b8735e
|
|
69
|
+
ds4-context-reference-adapter: f7d6e29393e248b89417d44b4c19e8c80cb684c2
|
|
70
|
+
ds4-context-engine: fd77ea3f0cc2885eff34b2702951d6dd2d4c3c14
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
For all three packages, npm `latest` now resolves to `0.3.2`, `beta` remains `0.3.0-beta.3`, `alpha` remains `0.3.0-alpha.5`, and `rc` remains `0.2.0-rc.1`.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# DS4 Context Engine 0.3.3
|
|
2
|
+
|
|
3
|
+
Status: candidate, not yet published.
|
|
4
|
+
|
|
5
|
+
This release adds a configurable transport retry policy for compaction summary requests on top of the 0.3.2 stable candidate. It carries forward 0.3.2 without changing canonical records, SQLite schema, runtime contracts, retention limits, privacy policy, compaction validation, or fallback semantics.
|
|
6
|
+
|
|
7
|
+
## Added since 0.3.2
|
|
8
|
+
|
|
9
|
+
- **Configurable compaction transport retry policy, mirroring Pi's assistant retry settings**:
|
|
10
|
+
- `compaction.transport.maxAttempts` (default `3`) and `compaction.transport.baseDelayMs` (default `2000` ms), with exponential backoff doubling per attempt, capped at 60 s per wait, and abort-aware: replay stops promptly when the request is cancelled;
|
|
11
|
+
- policy defaults mirror Pi (`retry.maxRetries` 3, `retry.baseDelayMs` 2000, exponential backoff);
|
|
12
|
+
- validated at config load: `maxAttempts` in `1..10`, `baseDelayMs` in `0..60000` ms; invalid values are rejected before use;
|
|
13
|
+
- `summary-generator` exposes `effectiveTransportPolicy` and `transportRetryDelayMs`, uses the configured `maxAttempts` in retry loops and diagnostics;
|
|
14
|
+
- `compaction-coordinator` threads `config.compaction.transport` through the summary request path;
|
|
15
|
+
- machine-readable catalog fields in `config-catalog.ts`, so the new keys are visible to `/context config view`;
|
|
16
|
+
- tests: unit coverage for policy defaults, backoff sequence, abort awareness, attempt counting, usage aggregation, and config validation; integration retry tests run with `baseDelayMs: 1` plus a new configurable-`maxAttempts` case; the frozen 0.2-line compatibility golden was updated to include the new transport defaults;
|
|
17
|
+
- `docs/COMPACTION.md`: new retry-policy section and revised bounds paragraph.
|
|
18
|
+
- Coordinated package versions moved to `0.3.3`.
|
|
19
|
+
|
|
20
|
+
There are no functional storage-format or provider-path changes relative to 0.3.2.
|
|
21
|
+
|
|
22
|
+
## Safety and compatibility
|
|
23
|
+
|
|
24
|
+
Unchanged from 0.3.2: Pi JSONL remains canonical and append-only; SQLite remains disposable and rebuildable; physical maintenance remains explicit, offline, local-TTY-only, and unavailable to model-callable tools; every `context_persistence` write still requires a fresh positive local UI decision.
|
|
25
|
+
|
|
26
|
+
Compatibility remains unchanged:
|
|
27
|
+
|
|
28
|
+
- SQLite schema: `15`;
|
|
29
|
+
- configuration: `ds4-context-config-v1` (new keys added: `compaction.transport.maxAttempts`, `compaction.transport.baseDelayMs`);
|
|
30
|
+
- runtime adapter: `runtime-adapter-v1`;
|
|
31
|
+
- persistence tool: `ds4-context-persistence-tool-v1`;
|
|
32
|
+
- persistence result: `ds4-context-persistence-result-v1`;
|
|
33
|
+
- Pi: `0.84.3`;
|
|
34
|
+
- Node.js: `>=22.19.0`.
|
|
35
|
+
|
|
36
|
+
## Package/version policy
|
|
37
|
+
|
|
38
|
+
The coordinated version is `0.3.3` for:
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
ds4-context-core
|
|
42
|
+
ds4-context-reference-adapter
|
|
43
|
+
ds4-context-engine
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Both adapters depend exactly on `ds4-context-core@0.3.3`. Publication uses npm's default `latest` tag for all three packages. GitHub Actions remains validation-only with OIDC and package-write permissions denied.
|
|
47
|
+
|
|
48
|
+
## Candidate validation
|
|
49
|
+
|
|
50
|
+
Local candidate verification on Node.js `26.5.1`:
|
|
51
|
+
|
|
52
|
+
- `npm run check`: 72 files and 376 tests passed (including the new transport-policy unit coverage, the configurable-`maxAttempts` integration case, and the updated golden freeze).
|
|
53
|
+
- `npm run quality:compare`: candidate quality versus the frozen baseline passed.
|
|
54
|
+
- `npm run schema:context-persistence`: within the 1,500 / 320 absolute and relative limits.
|
|
55
|
+
- `npm run latency:check` against the frozen baseline passed (`regressionRatio` `1.040189`, maximum `1.1`).
|
|
56
|
+
- `npm run pack:check` and `npm pack --dry-run --json` for all three packages passed with no forbidden local/session/storage paths.
|
|
57
|
+
- `git diff --check` passed; the only pre-existing untracked path is `.serena/`, which is excluded from commits and package inventories.
|
|
58
|
+
- Version, exact core dependencies, package-lock entries, extension constant, and reference-adapter constant are synchronized to `0.3.3`.
|
|
59
|
+
|
|
60
|
+
Validation-only CI is recorded below with the release commit. Exact registry verification and the annotated tag are recorded after execution.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ds4-context-engine",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Non-destructive, provider-independent context management for Pi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
]
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"ds4-context-core": "0.3.
|
|
65
|
+
"ds4-context-core": "0.3.3"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
68
|
"@earendil-works/pi-ai": "0.84.3",
|
|
@@ -1005,6 +1005,7 @@ export class CompactionCoordinator {
|
|
|
1005
1005
|
...input,
|
|
1006
1006
|
validate: this.dependencies.config.compaction.validate,
|
|
1007
1007
|
maxSummaryTokens: this.dependencies.config.context.maxSummaryTokens,
|
|
1008
|
+
transport: this.dependencies.config.compaction.transport,
|
|
1008
1009
|
now: this.dependencies.now,
|
|
1009
1010
|
onTransportRetry: (diagnostic) => {
|
|
1010
1011
|
this.state.transportRetries = (this.state.transportRetries ?? 0) + 1;
|
|
@@ -13,8 +13,39 @@ import {
|
|
|
13
13
|
type SummaryValidationResult,
|
|
14
14
|
} from "ds4-context-core/compaction/summary-contract";
|
|
15
15
|
|
|
16
|
-
export const
|
|
17
|
-
const
|
|
16
|
+
export const DEFAULT_COMPACTION_TRANSPORT_MAX_ATTEMPTS = 3;
|
|
17
|
+
export const DEFAULT_COMPACTION_TRANSPORT_BASE_DELAY_MS = 2000;
|
|
18
|
+
export const COMPACTION_TRANSPORT_MAX_DELAY_MS = 60_000;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Transport retry policy for compaction summary requests. Defaults mirror Pi's
|
|
22
|
+
* assistant retry settings (`retry.maxRetries` 3, `retry.baseDelayMs` 2000,
|
|
23
|
+
* exponential backoff, abort-aware).
|
|
24
|
+
*/
|
|
25
|
+
export interface CompactionTransportPolicy {
|
|
26
|
+
/** Total attempts for transport-classified failures. Default: 3. */
|
|
27
|
+
maxAttempts?: number;
|
|
28
|
+
/** Base backoff delay in ms, doubled per attempt. Default: 2000. */
|
|
29
|
+
baseDelayMs?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function effectiveTransportPolicy(
|
|
33
|
+
policy: CompactionTransportPolicy | undefined,
|
|
34
|
+
): { maxAttempts: number; baseDelayMs: number } {
|
|
35
|
+
const maxAttempts = Math.min(
|
|
36
|
+
10,
|
|
37
|
+
Math.max(1, policy?.maxAttempts ?? DEFAULT_COMPACTION_TRANSPORT_MAX_ATTEMPTS),
|
|
38
|
+
);
|
|
39
|
+
const baseDelayMs = Math.min(
|
|
40
|
+
COMPACTION_TRANSPORT_MAX_DELAY_MS,
|
|
41
|
+
Math.max(0, policy?.baseDelayMs ?? DEFAULT_COMPACTION_TRANSPORT_BASE_DELAY_MS),
|
|
42
|
+
);
|
|
43
|
+
return { maxAttempts, baseDelayMs };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function transportRetryDelayMs(baseDelayMs: number, failedAttempt: number): number {
|
|
47
|
+
return Math.min(COMPACTION_TRANSPORT_MAX_DELAY_MS, baseDelayMs * 2 ** (failedAttempt - 1));
|
|
48
|
+
}
|
|
18
49
|
|
|
19
50
|
export interface CompactionTransportRetryDiagnostic {
|
|
20
51
|
stage: "segment" | "aggregate";
|
|
@@ -38,6 +69,8 @@ export interface GenerateValidatedSummaryInput {
|
|
|
38
69
|
model?: Model<Api>;
|
|
39
70
|
/** Reasoning level for the summary request; `off` (default) keeps the pre-existing request shape. */
|
|
40
71
|
thinking?: CompactionThinkingLevel;
|
|
72
|
+
/** Transport retry policy; defaults mirror Pi's assistant retry settings. */
|
|
73
|
+
transport?: CompactionTransportPolicy;
|
|
41
74
|
now: () => number;
|
|
42
75
|
onTransportRetry?: (diagnostic: CompactionTransportRetryDiagnostic) => void;
|
|
43
76
|
}
|
|
@@ -162,6 +195,7 @@ export async function generateValidatedSummary(
|
|
|
162
195
|
const model = input.model ?? input.ctx.model;
|
|
163
196
|
if (!model) throw new Error("Compaction summary generation requires an active model");
|
|
164
197
|
const maxTokens = Math.max(1, Math.min(input.maxSummaryTokens, model.maxTokens ?? input.maxSummaryTokens));
|
|
198
|
+
const { maxAttempts, baseDelayMs } = effectiveTransportPolicy(input.transport);
|
|
165
199
|
const retryUsages: Usage[] = [];
|
|
166
200
|
let response: Awaited<ReturnType<typeof input.ctx.modelRegistry.complete>>;
|
|
167
201
|
let attempt = 0;
|
|
@@ -189,18 +223,18 @@ export async function generateValidatedSummary(
|
|
|
189
223
|
} catch (error) {
|
|
190
224
|
if (input.event.signal.aborted) throw abortedError();
|
|
191
225
|
const category = providerFailureCategory(error);
|
|
192
|
-
if (category !== "transport" || attempt >=
|
|
226
|
+
if (category !== "transport" || attempt >= maxAttempts) {
|
|
193
227
|
throw new Error(
|
|
194
228
|
`Compaction ${input.stage} request failed (${transportFailureSuffix(category, attempt)})`,
|
|
195
229
|
);
|
|
196
230
|
}
|
|
197
|
-
const delayMs =
|
|
231
|
+
const delayMs = transportRetryDelayMs(baseDelayMs, attempt);
|
|
198
232
|
await waitForTransportRetry(input.event.signal, delayMs);
|
|
199
233
|
input.onTransportRetry?.({
|
|
200
234
|
stage: input.stage,
|
|
201
235
|
failedAttempt: attempt,
|
|
202
236
|
nextAttempt: attempt + 1,
|
|
203
|
-
maxAttempts
|
|
237
|
+
maxAttempts,
|
|
204
238
|
delayMs,
|
|
205
239
|
});
|
|
206
240
|
continue;
|
|
@@ -209,15 +243,15 @@ export async function generateValidatedSummary(
|
|
|
209
243
|
const stopReason = responseStopReason(response);
|
|
210
244
|
if (stopReason === "error") {
|
|
211
245
|
const category = providerFailureCategory(responseErrorMessage(response));
|
|
212
|
-
if (category === "transport" && attempt <
|
|
246
|
+
if (category === "transport" && attempt < maxAttempts) {
|
|
213
247
|
retryUsages.push(response.usage);
|
|
214
|
-
const delayMs =
|
|
248
|
+
const delayMs = transportRetryDelayMs(baseDelayMs, attempt);
|
|
215
249
|
await waitForTransportRetry(input.event.signal, delayMs);
|
|
216
250
|
input.onTransportRetry?.({
|
|
217
251
|
stage: input.stage,
|
|
218
252
|
failedAttempt: attempt,
|
|
219
253
|
nextAttempt: attempt + 1,
|
|
220
|
-
maxAttempts
|
|
254
|
+
maxAttempts,
|
|
221
255
|
delayMs,
|
|
222
256
|
});
|
|
223
257
|
continue;
|