ds4-context-engine 0.3.0-alpha.5 → 0.3.0-beta.2
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 +24 -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 +6 -6
- package/docs/STORAGE.md +16 -2
- package/docs/STORAGE_MAINTENANCE.md +129 -0
- package/docs/releases/0.3.0-alpha.5.md +5 -3
- package/docs/releases/0.3.0-beta.1.md +90 -0
- package/docs/releases/0.3.0-beta.2.md +84 -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
package/src/extension/runtime.ts
CHANGED
|
@@ -66,6 +66,11 @@ import type {
|
|
|
66
66
|
PrivacyManifest,
|
|
67
67
|
ProviderUsageManifest,
|
|
68
68
|
} from "ds4-context-core/manifest/context-manifest";
|
|
69
|
+
import { HARD_PERSISTED_MANIFEST_BYTES } from "ds4-context-core/manifest/context-manifest-storage";
|
|
70
|
+
import {
|
|
71
|
+
unavailableStorageDiagnostics,
|
|
72
|
+
type StorageDiagnostics,
|
|
73
|
+
} from "ds4-context-core/persistence/storage-diagnostics";
|
|
69
74
|
import type { ExcludedContextSource, ObservedTool } from "ds4-context-core/manifest/observer";
|
|
70
75
|
import {
|
|
71
76
|
disabledCrossSessionMemoryDiagnostics,
|
|
@@ -133,6 +138,7 @@ import {
|
|
|
133
138
|
} from "ds4-context-core/retrieval/retrieval-engine";
|
|
134
139
|
import { currentRequestText } from "ds4-context-core/retrieval/task-descriptor";
|
|
135
140
|
import { LocalFeatureHashEmbedding } from "../pi-adapter/local-embedding.ts";
|
|
141
|
+
import { DatabaseProtocolError } from "ds4-context-core/persistence/database-client-lease";
|
|
136
142
|
import { ContextDatabase, type DatabaseHealth, type SessionIndexStats } from "ds4-context-core/persistence/sqlite";
|
|
137
143
|
import {
|
|
138
144
|
activeTools,
|
|
@@ -371,6 +377,7 @@ export class Ds4ContextRuntime {
|
|
|
371
377
|
private observation?: ContextObservation;
|
|
372
378
|
private lastManifest?: ContextManifest;
|
|
373
379
|
private pendingManifestId?: string;
|
|
380
|
+
private pendingManifestPersisted = false;
|
|
374
381
|
private retrievalEngine?: HistoricalRetrievalEngine;
|
|
375
382
|
private lastRetrieval: RetrievalDiagnostics = emptyRetrievalDiagnostics();
|
|
376
383
|
private semanticIndex?: SemanticEmbeddingIndex;
|
|
@@ -440,6 +447,7 @@ export class Ds4ContextRuntime {
|
|
|
440
447
|
this.lastIndexResult = undefined;
|
|
441
448
|
this.lastManifest = undefined;
|
|
442
449
|
this.pendingManifestId = undefined;
|
|
450
|
+
this.pendingManifestPersisted = false;
|
|
443
451
|
this.retrievalEngine = undefined;
|
|
444
452
|
this.semanticIndex = undefined;
|
|
445
453
|
this.semanticFallbackReason = undefined;
|
|
@@ -1228,9 +1236,7 @@ export class Ds4ContextRuntime {
|
|
|
1228
1236
|
}
|
|
1229
1237
|
this.lastManifest = manifest;
|
|
1230
1238
|
this.pendingManifestId = manifest.id;
|
|
1231
|
-
|
|
1232
|
-
this.database?.manifests.save(manifest);
|
|
1233
|
-
}
|
|
1239
|
+
this.pendingManifestPersisted = this.persistManifest(manifest);
|
|
1234
1240
|
|
|
1235
1241
|
this.logger.trace("context.planned", {
|
|
1236
1242
|
sessionId: this.session?.sessionId,
|
|
@@ -1311,6 +1317,47 @@ export class Ds4ContextRuntime {
|
|
|
1311
1317
|
}
|
|
1312
1318
|
}
|
|
1313
1319
|
|
|
1320
|
+
private persistManifest(manifest: ContextManifest): boolean {
|
|
1321
|
+
if (!this.config.diagnostics.storeContextManifest || !this.session?.sessionFile || !this.database) {
|
|
1322
|
+
return false;
|
|
1323
|
+
}
|
|
1324
|
+
try {
|
|
1325
|
+
const result = this.database.manifests.save(manifest);
|
|
1326
|
+
if (result.status === "skipped-oversize") {
|
|
1327
|
+
this.logger.warn("context.manifest_persistence_skipped", {
|
|
1328
|
+
category: "oversize",
|
|
1329
|
+
sourceBytes: result.sourceBytes,
|
|
1330
|
+
projectedBytes: result.projectedBytes,
|
|
1331
|
+
includedCount: manifest.included.length,
|
|
1332
|
+
excludedCount: manifest.excluded.length,
|
|
1333
|
+
hardLimitBytes: HARD_PERSISTED_MANIFEST_BYTES,
|
|
1334
|
+
});
|
|
1335
|
+
return false;
|
|
1336
|
+
}
|
|
1337
|
+
this.logger.debug("context.manifest_persisted", {
|
|
1338
|
+
manifestId: manifest.id,
|
|
1339
|
+
completeness: result.completeness,
|
|
1340
|
+
sourceBytes: result.sourceBytes,
|
|
1341
|
+
storedBytes: result.storedBytes,
|
|
1342
|
+
});
|
|
1343
|
+
if (result.prunedManifests > 0) {
|
|
1344
|
+
this.logger.debug("database.retention_progress", {
|
|
1345
|
+
category: "context-manifest",
|
|
1346
|
+
prunedRows: result.prunedManifests,
|
|
1347
|
+
prunedBytes: result.prunedBytes,
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
return true;
|
|
1351
|
+
} catch (error) {
|
|
1352
|
+
this.logger.warn("context.manifest_persistence_skipped", {
|
|
1353
|
+
category: "storage-write",
|
|
1354
|
+
manifestId: manifest.id,
|
|
1355
|
+
errorType: error instanceof Error ? error.name : "UnknownError",
|
|
1356
|
+
});
|
|
1357
|
+
return false;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1314
1361
|
private updateManifestPrivacy(): void {
|
|
1315
1362
|
if (!this.lastManifest?.privacy) return;
|
|
1316
1363
|
const updatedPrivacy = this.privacyManifest(
|
|
@@ -1322,8 +1369,9 @@ export class Ds4ContextRuntime {
|
|
|
1322
1369
|
);
|
|
1323
1370
|
if (!updatedPrivacy) return;
|
|
1324
1371
|
this.lastManifest = { ...this.lastManifest, privacy: updatedPrivacy };
|
|
1325
|
-
|
|
1326
|
-
|
|
1372
|
+
const persisted = this.persistManifest(this.lastManifest);
|
|
1373
|
+
if (this.pendingManifestId === this.lastManifest.id) {
|
|
1374
|
+
this.pendingManifestPersisted = this.pendingManifestPersisted || persisted;
|
|
1327
1375
|
}
|
|
1328
1376
|
}
|
|
1329
1377
|
|
|
@@ -1453,14 +1501,9 @@ export class Ds4ContextRuntime {
|
|
|
1453
1501
|
...this.lastManifest,
|
|
1454
1502
|
nativeContinuation: { ...continuation },
|
|
1455
1503
|
};
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
this.
|
|
1459
|
-
} catch (error) {
|
|
1460
|
-
this.logger.warn("native_continuation.manifest_persistence_failed", {
|
|
1461
|
-
manifestId,
|
|
1462
|
-
errorType: error instanceof Error ? error.name : "UnknownError",
|
|
1463
|
-
});
|
|
1504
|
+
const persisted = this.persistManifest(this.lastManifest);
|
|
1505
|
+
if (this.pendingManifestId === manifestId) {
|
|
1506
|
+
this.pendingManifestPersisted = this.pendingManifestPersisted || persisted;
|
|
1464
1507
|
}
|
|
1465
1508
|
}
|
|
1466
1509
|
|
|
@@ -1470,7 +1513,9 @@ export class Ds4ContextRuntime {
|
|
|
1470
1513
|
if (record.role !== "assistant") return;
|
|
1471
1514
|
|
|
1472
1515
|
const manifestId = this.pendingManifestId;
|
|
1516
|
+
const manifestPersisted = this.pendingManifestPersisted;
|
|
1473
1517
|
this.pendingManifestId = undefined;
|
|
1518
|
+
this.pendingManifestPersisted = false;
|
|
1474
1519
|
if (record.stopReason === "error" || record.stopReason === "aborted") return;
|
|
1475
1520
|
const usage = record.usage;
|
|
1476
1521
|
if (!usage || typeof usage !== "object") return;
|
|
@@ -1491,19 +1536,17 @@ export class Ds4ContextRuntime {
|
|
|
1491
1536
|
};
|
|
1492
1537
|
}
|
|
1493
1538
|
const createdAt = this.now();
|
|
1494
|
-
const persisted = Boolean(
|
|
1495
|
-
this.config.diagnostics.storeContextManifest
|
|
1496
|
-
&& this.session?.sessionFile
|
|
1497
|
-
&& this.database,
|
|
1498
|
-
);
|
|
1499
1539
|
try {
|
|
1500
|
-
if (
|
|
1501
|
-
const updated = this.database
|
|
1540
|
+
if (manifestPersisted && this.database) {
|
|
1541
|
+
const updated = this.database.manifests.recordProviderUsage(
|
|
1502
1542
|
manifestId,
|
|
1503
1543
|
providerUsage,
|
|
1504
1544
|
createdAt,
|
|
1545
|
+
manifest?.modelAwareness?.calibration.estimator ?? "chars-v1",
|
|
1505
1546
|
);
|
|
1506
|
-
if (updated &&
|
|
1547
|
+
if (!updated && manifest?.estimatedInputTokens) {
|
|
1548
|
+
this.rememberVolatileCalibration(manifest, providerUsage, createdAt);
|
|
1549
|
+
}
|
|
1507
1550
|
} else if (manifest?.estimatedInputTokens) {
|
|
1508
1551
|
this.rememberVolatileCalibration(manifest, providerUsage, createdAt);
|
|
1509
1552
|
}
|
|
@@ -1513,7 +1556,8 @@ export class Ds4ContextRuntime {
|
|
|
1513
1556
|
}
|
|
1514
1557
|
this.logger.warn("context.usage_persistence_failed", {
|
|
1515
1558
|
manifestId,
|
|
1516
|
-
|
|
1559
|
+
category: "storage-write",
|
|
1560
|
+
errorType: error instanceof Error ? error.name : "UnknownError",
|
|
1517
1561
|
});
|
|
1518
1562
|
}
|
|
1519
1563
|
this.logger.debug("context.actual_usage_recorded", {
|
|
@@ -2913,6 +2957,11 @@ export class Ds4ContextRuntime {
|
|
|
2913
2957
|
return this.database.health();
|
|
2914
2958
|
}
|
|
2915
2959
|
|
|
2960
|
+
storageDiagnostics(): StorageDiagnostics {
|
|
2961
|
+
return this.database?.storageDiagnostics(this.session?.projectPath)
|
|
2962
|
+
?? unavailableStorageDiagnostics();
|
|
2963
|
+
}
|
|
2964
|
+
|
|
2916
2965
|
shutdown(ctx?: ExtensionContext): void {
|
|
2917
2966
|
if (ctx) this.syncSessionIndex(ctx);
|
|
2918
2967
|
this.flushContextQuality();
|
|
@@ -2931,8 +2980,16 @@ export class Ds4ContextRuntime {
|
|
|
2931
2980
|
this.nativeContinuation.invalidate("runtime-degraded");
|
|
2932
2981
|
this.closeDatabase();
|
|
2933
2982
|
this.phase = "degraded";
|
|
2934
|
-
|
|
2935
|
-
|
|
2983
|
+
const maintenanceActive = error instanceof DatabaseProtocolError && error.category === "maintenance-active";
|
|
2984
|
+
const errorType = error instanceof Error ? error.name : "UnknownError";
|
|
2985
|
+
this.lastError = maintenanceActive
|
|
2986
|
+
? "Database maintenance is active (category=maintenance-active)"
|
|
2987
|
+
: `Database startup unavailable (category=storage-unavailable; errorType=${errorType})`;
|
|
2988
|
+
if (maintenanceActive) {
|
|
2989
|
+
this.logger.warn("database.maintenance_active", { category: "maintenance-active" });
|
|
2990
|
+
} else {
|
|
2991
|
+
this.logger.error("runtime.degraded", { stage, category: "storage-unavailable", errorType });
|
|
2992
|
+
}
|
|
2936
2993
|
this.setStatus(ctx, "DS4 ctx: Pi fallback");
|
|
2937
2994
|
if (ctx.hasUI) {
|
|
2938
2995
|
ctx.ui.notify(`DS4 Context Engine unavailable; using Pi context. ${this.lastError}`, "warning");
|
|
@@ -81,6 +81,7 @@ export interface CompactionDiagnostics {
|
|
|
81
81
|
sourcePromptTokens?: number;
|
|
82
82
|
segmentCount?: number;
|
|
83
83
|
aggregateCalls?: number;
|
|
84
|
+
transportRetries?: number;
|
|
84
85
|
contextTokens?: number;
|
|
85
86
|
softLimitTokens?: number;
|
|
86
87
|
proactiveThresholdTokens?: number;
|
|
@@ -224,6 +225,7 @@ export class CompactionCoordinator {
|
|
|
224
225
|
requestedAt,
|
|
225
226
|
firstKeptEntryId: event.preparation.firstKeptEntryId,
|
|
226
227
|
tokensBefore: event.preparation.tokensBefore,
|
|
228
|
+
transportRetries: 0,
|
|
227
229
|
};
|
|
228
230
|
|
|
229
231
|
try {
|
|
@@ -357,6 +359,7 @@ export class CompactionCoordinator {
|
|
|
357
359
|
sourcePromptTokens: wholePlan.promptTokens,
|
|
358
360
|
segmentCount: segmentPlans.length,
|
|
359
361
|
aggregateCalls: aggregated.aggregateCalls,
|
|
362
|
+
transportRetries: this.state.transportRetries ?? 0,
|
|
360
363
|
};
|
|
361
364
|
this.dependencies.logger.debug("compaction.summary_graph_prepared", {
|
|
362
365
|
activeSummaryId: activeNode.id,
|
|
@@ -369,6 +372,7 @@ export class CompactionCoordinator {
|
|
|
369
372
|
sourcePromptTokens: wholePlan.promptTokens,
|
|
370
373
|
segmentCount: segmentPlans.length,
|
|
371
374
|
aggregateCalls: aggregated.aggregateCalls,
|
|
375
|
+
transportRetries: this.state.transportRetries ?? 0,
|
|
372
376
|
trigger,
|
|
373
377
|
});
|
|
374
378
|
const activeRecord = records.at(-1);
|
|
@@ -435,6 +439,7 @@ export class CompactionCoordinator {
|
|
|
435
439
|
...(attempt.sourcePromptTokens !== undefined ? { sourcePromptTokens: attempt.sourcePromptTokens } : {}),
|
|
436
440
|
...(attempt.segmentCount !== undefined ? { segmentCount: attempt.segmentCount } : {}),
|
|
437
441
|
...(attempt.aggregateCalls !== undefined ? { aggregateCalls: attempt.aggregateCalls } : {}),
|
|
442
|
+
...(attempt.transportRetries !== undefined ? { transportRetries: attempt.transportRetries } : {}),
|
|
438
443
|
...(customError ? { lastError: `Custom fallback: ${customError}` } : {}),
|
|
439
444
|
};
|
|
440
445
|
return;
|
|
@@ -475,6 +480,9 @@ export class CompactionCoordinator {
|
|
|
475
480
|
: {}),
|
|
476
481
|
...(this.state.segmentCount !== undefined ? { segmentCount: this.state.segmentCount } : {}),
|
|
477
482
|
...(this.state.aggregateCalls !== undefined ? { aggregateCalls: this.state.aggregateCalls } : {}),
|
|
483
|
+
...(this.state.transportRetries !== undefined
|
|
484
|
+
? { transportRetries: this.state.transportRetries }
|
|
485
|
+
: {}),
|
|
478
486
|
};
|
|
479
487
|
this.dependencies.logger.debug("compaction.summary_graph_committed", {
|
|
480
488
|
activeSummaryId: metadata.summaryId,
|
|
@@ -950,6 +958,10 @@ export class CompactionCoordinator {
|
|
|
950
958
|
validate: this.dependencies.config.compaction.validate,
|
|
951
959
|
maxSummaryTokens: this.dependencies.config.context.maxSummaryTokens,
|
|
952
960
|
now: this.dependencies.now,
|
|
961
|
+
onTransportRetry: (diagnostic) => {
|
|
962
|
+
this.state.transportRetries = (this.state.transportRetries ?? 0) + 1;
|
|
963
|
+
this.dependencies.logger.debug("compaction.transport_retry", { ...diagnostic });
|
|
964
|
+
},
|
|
953
965
|
});
|
|
954
966
|
}
|
|
955
967
|
|
|
@@ -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.2";
|
|
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";
|