@wrongstack/core 0.306.3 → 0.306.4
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/dist/coordination/index.js +136 -40
- package/dist/coordination/mail-tools.d.ts +1 -1
- package/dist/core/conversation-state.d.ts +1 -2
- package/dist/core/fallback-model.d.ts +19 -1
- package/dist/core/fallback-profile-manager.d.ts +21 -3
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +152 -46
- package/dist/defaults/index.js +295 -91
- package/dist/execution/auto-compaction-middleware.d.ts +64 -0
- package/dist/execution/compaction-core.d.ts +4 -0
- package/dist/execution/index.d.ts +1 -1
- package/dist/execution/index.js +230 -89
- package/dist/execution/retry-policy.d.ts +27 -0
- package/dist/hq/index.js +50 -15
- package/dist/index.d.ts +1 -1
- package/dist/index.js +588 -134
- package/dist/infrastructure/index.js +21 -0
- package/dist/plugin/index.js +195 -19
- package/dist/security/index.js +50 -15
- package/dist/session-catalog/index.js +50 -15
- package/dist/session-catalog/project-server.js +50 -15
- package/dist/storage/cloud-config-sync/sanitize.d.ts +18 -0
- package/dist/storage/cloud-config-sync.d.ts +1 -1
- package/dist/storage/index.js +276 -23
- package/dist/storage/provider-config-watcher.d.ts +9 -0
- package/dist/storage/session-store/strict-empty-check.d.ts +7 -0
- package/dist/storage/session-store.d.ts +1 -0
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.js +83 -22
- package/dist/types/config/root.d.ts +14 -0
- package/dist/types/session.d.ts +7 -0
- package/package.json +3 -3
package/dist/defaults/index.js
CHANGED
|
@@ -9828,6 +9828,24 @@ import {
|
|
|
9828
9828
|
} from "@wrongstack/persistence";
|
|
9829
9829
|
|
|
9830
9830
|
// src/security/secret-scrubber.ts
|
|
9831
|
+
var JSON_CREDENTIAL_KEY_ANCHORS = [
|
|
9832
|
+
'Key"',
|
|
9833
|
+
'key"',
|
|
9834
|
+
'KEY"',
|
|
9835
|
+
'token"',
|
|
9836
|
+
'Token"',
|
|
9837
|
+
'TOKEN"',
|
|
9838
|
+
'secret"',
|
|
9839
|
+
'Secret"',
|
|
9840
|
+
'SECRET"',
|
|
9841
|
+
'password"',
|
|
9842
|
+
'Password"',
|
|
9843
|
+
'PASSWORD"',
|
|
9844
|
+
'authorization"',
|
|
9845
|
+
'Authorization"',
|
|
9846
|
+
'bearer"',
|
|
9847
|
+
'Bearer"'
|
|
9848
|
+
];
|
|
9831
9849
|
var PATTERNS = [
|
|
9832
9850
|
// Anchored at the start where possible so partial matches inside larger
|
|
9833
9851
|
// strings don't trigger false positives.
|
|
@@ -9930,6 +9948,30 @@ var PATTERNS = [
|
|
|
9930
9948
|
regex: /(^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?=\s|$)/g,
|
|
9931
9949
|
anchor: ["KEY", "TOKEN", "SECRET", "PASSWORD", "PWD"]
|
|
9932
9950
|
},
|
|
9951
|
+
{
|
|
9952
|
+
type: "json_credential_key",
|
|
9953
|
+
// The JSON counterpart to `high_entropy_env`, and the pattern that the
|
|
9954
|
+
// now-deleted `JSON_KEY_ANCHORS` list was written for. Without it those
|
|
9955
|
+
// anchors only widened the cheap pre-scan — `hasCredentialAnchors` said
|
|
9956
|
+
// "this text may hold a secret", every pattern then declined to match, and
|
|
9957
|
+
// the value went out verbatim. `high_entropy_env` cannot cover these: it
|
|
9958
|
+
// requires an UPPERCASE unquoted key (`API_KEY=…`), so `{"apiKey":"…"}`
|
|
9959
|
+
// never matched.
|
|
9960
|
+
//
|
|
9961
|
+
// Tool results are routinely serialised as JSON, and a credential with no
|
|
9962
|
+
// recognisable prefix (Azure, self-hosted gateways, Anthropic/Codex OAuth)
|
|
9963
|
+
// has no other pattern that can catch it — this is the only thing standing
|
|
9964
|
+
// between such a value and the session JSONL, chronicle, HQ broadcast and
|
|
9965
|
+
// the model's own context.
|
|
9966
|
+
//
|
|
9967
|
+
// The key may carry a prefix (`"anthropicApiKey"`), but the credential word
|
|
9968
|
+
// must END the key: `"tokenCount"` and `"maxTokens"` do not match, because
|
|
9969
|
+
// the closing quote has to follow the word immediately.
|
|
9970
|
+
// Value floor of 8 chars keeps enum-ish values (`"authorization":"none"`)
|
|
9971
|
+
// out. Capture groups: 1=key + punctuation, 2=value, 3=closing quote.
|
|
9972
|
+
regex: /("[A-Za-z0-9_]*(?:apiKey|api_key|token|secret|password|authorization|bearer|private_key|access_token|refresh_token|client_secret)"\s*:\s*")([^"\\]{8,512})(")/gi,
|
|
9973
|
+
anchor: JSON_CREDENTIAL_KEY_ANCHORS
|
|
9974
|
+
},
|
|
9933
9975
|
// ── Ported from packages/plugins credential-patterns.ts (WS-034) ─────────
|
|
9934
9976
|
// The plugin runtime carried 37 patterns while this scrubber — the one that
|
|
9935
9977
|
// guards session JSONL, chronicle, HQ broadcast, WebUI events and the auth
|
|
@@ -10012,9 +10054,12 @@ var PATTERNS = [
|
|
|
10012
10054
|
anchor: "GOCSPX-"
|
|
10013
10055
|
}
|
|
10014
10056
|
];
|
|
10015
|
-
var SIMPLE_PATTERNS = PATTERNS.filter(
|
|
10057
|
+
var SIMPLE_PATTERNS = PATTERNS.filter(
|
|
10058
|
+
(p) => p.type !== "high_entropy_env" && p.type !== "json_credential_key"
|
|
10059
|
+
);
|
|
10016
10060
|
var COMBINED_REGEX = new RegExp(SIMPLE_PATTERNS.map((p) => `(${p.regex.source})`).join("|"), "g");
|
|
10017
10061
|
var HIGH_ENTROPY_REGEX = PATTERNS.find((p) => p.type === "high_entropy_env").regex;
|
|
10062
|
+
var JSON_CREDENTIAL_REGEX = PATTERNS.find((p) => p.type === "json_credential_key").regex;
|
|
10018
10063
|
var COMBINED_REPLACEMENTS = SIMPLE_PATTERNS.map((p) => `[REDACTED:${p.type}]`);
|
|
10019
10064
|
var SCRUB_CHUNK_BYTES = 64 * 1024;
|
|
10020
10065
|
var SCRUB_OVERLAP_BYTES = 1024;
|
|
@@ -10025,20 +10070,7 @@ var PATTERN_ANCHORS = [
|
|
|
10025
10070
|
)
|
|
10026
10071
|
)
|
|
10027
10072
|
];
|
|
10028
|
-
var
|
|
10029
|
-
'"apiKey"',
|
|
10030
|
-
'"api_key"',
|
|
10031
|
-
'"token"',
|
|
10032
|
-
'"secret"',
|
|
10033
|
-
'"password"',
|
|
10034
|
-
'"authorization"',
|
|
10035
|
-
'"bearer"',
|
|
10036
|
-
'"private_key"',
|
|
10037
|
-
'"access_token"',
|
|
10038
|
-
'"refresh_token"',
|
|
10039
|
-
'"client_secret"'
|
|
10040
|
-
];
|
|
10041
|
-
var ALL_ANCHORS = [...PATTERN_ANCHORS, ...JSON_KEY_ANCHORS];
|
|
10073
|
+
var ALL_ANCHORS = PATTERN_ANCHORS;
|
|
10042
10074
|
function hasCredentialAnchors(text) {
|
|
10043
10075
|
for (const anchor of ALL_ANCHORS) {
|
|
10044
10076
|
if (text.includes(anchor)) return true;
|
|
@@ -10087,6 +10119,9 @@ var DefaultSecretScrubber = class {
|
|
|
10087
10119
|
out = out.replace(HIGH_ENTROPY_REGEX, (_match, lead, key, _value) => {
|
|
10088
10120
|
return `${lead}${key}=[REDACTED:high_entropy_env]`;
|
|
10089
10121
|
});
|
|
10122
|
+
out = out.replace(JSON_CREDENTIAL_REGEX, (_match, keyPrefix, _value, closingQuote) => {
|
|
10123
|
+
return `${keyPrefix}[REDACTED:json_credential_key]${closingQuote}`;
|
|
10124
|
+
});
|
|
10090
10125
|
return out;
|
|
10091
10126
|
}
|
|
10092
10127
|
/**
|
|
@@ -11017,6 +11052,7 @@ function isEmptyMessage(msg) {
|
|
|
11017
11052
|
}
|
|
11018
11053
|
|
|
11019
11054
|
// src/core/context.ts
|
|
11055
|
+
import { realpathSync } from "node:fs";
|
|
11020
11056
|
import * as path13 from "node:path";
|
|
11021
11057
|
|
|
11022
11058
|
// src/utils/tool-wire-compact.ts
|
|
@@ -11446,12 +11482,15 @@ var ConversationState = class {
|
|
|
11446
11482
|
* cap determines the starting index for the sum but does not gate it.
|
|
11447
11483
|
*/
|
|
11448
11484
|
overflowCount(arr) {
|
|
11449
|
-
|
|
11450
|
-
|
|
11485
|
+
const contextClass = this.ctx.constructor;
|
|
11486
|
+
const maxMessages = contextClass.MAX_MESSAGES;
|
|
11487
|
+
const maxMessageTokens = contextClass.MAX_MESSAGE_TOKENS;
|
|
11488
|
+
let drop = maxMessages > 0 ? Math.max(0, arr.length - maxMessages) : 0;
|
|
11489
|
+
if (maxMessageTokens <= 0) return this.protocolSafeDropCount(arr, drop);
|
|
11451
11490
|
let total = 0;
|
|
11452
11491
|
for (let i = drop; i < arr.length; i++) total += arr[i]?._estTokens ?? 0;
|
|
11453
|
-
if (total <=
|
|
11454
|
-
while (drop < arr.length - 1 && total >
|
|
11492
|
+
if (total <= maxMessageTokens) return this.protocolSafeDropCount(arr, drop);
|
|
11493
|
+
while (drop < arr.length - 1 && total > maxMessageTokens) {
|
|
11455
11494
|
total -= arr[drop]?._estTokens ?? 0;
|
|
11456
11495
|
drop++;
|
|
11457
11496
|
}
|
|
@@ -12274,6 +12313,19 @@ var Context = class _Context {
|
|
|
12274
12313
|
if (rel.startsWith("..") || path13.isAbsolute(rel)) {
|
|
12275
12314
|
throw new Error(`Working directory "${resolved}" is outside project root "${root}"`);
|
|
12276
12315
|
}
|
|
12316
|
+
let realTarget = resolved;
|
|
12317
|
+
let realRoot = root;
|
|
12318
|
+
try {
|
|
12319
|
+
realTarget = realpathSync.native(resolved);
|
|
12320
|
+
realRoot = realpathSync.native(root);
|
|
12321
|
+
} catch {
|
|
12322
|
+
}
|
|
12323
|
+
const realRel = path13.relative(realRoot, realTarget);
|
|
12324
|
+
if (realRel.startsWith("..") || path13.isAbsolute(realRel)) {
|
|
12325
|
+
throw new Error(
|
|
12326
|
+
`Working directory "${resolved}" resolves to "${realTarget}", outside project root "${realRoot}"`
|
|
12327
|
+
);
|
|
12328
|
+
}
|
|
12277
12329
|
}
|
|
12278
12330
|
const old = this.workingDir;
|
|
12279
12331
|
this.workingDir = resolved;
|
|
@@ -16526,9 +16578,40 @@ async function readOrBuildShardManifestEntry(opts) {
|
|
|
16526
16578
|
return entry;
|
|
16527
16579
|
}
|
|
16528
16580
|
|
|
16529
|
-
// src/storage/session-store/
|
|
16581
|
+
// src/storage/session-store/strict-empty-check.ts
|
|
16530
16582
|
import { createReadStream as createReadStream3 } from "node:fs";
|
|
16531
16583
|
import { createInterface as createInterface3 } from "node:readline";
|
|
16584
|
+
var EMPTY_SESSION_EVENT_TYPES = /* @__PURE__ */ new Set(["session_start", "session_resumed", "session_end"]);
|
|
16585
|
+
async function isStrictlyEmptySessionFile(file) {
|
|
16586
|
+
const input = createReadStream3(file, { encoding: "utf8" });
|
|
16587
|
+
const lines = createInterface3({ input, crlfDelay: Infinity });
|
|
16588
|
+
let sawSessionStart = false;
|
|
16589
|
+
try {
|
|
16590
|
+
for await (const line of lines) {
|
|
16591
|
+
if (!line.trim()) continue;
|
|
16592
|
+
let event;
|
|
16593
|
+
try {
|
|
16594
|
+
event = JSON.parse(line);
|
|
16595
|
+
} catch {
|
|
16596
|
+
return false;
|
|
16597
|
+
}
|
|
16598
|
+
if (event === null || typeof event !== "object" || Array.isArray(event)) return false;
|
|
16599
|
+
const type = event.type;
|
|
16600
|
+
if (typeof type !== "string" || !EMPTY_SESSION_EVENT_TYPES.has(type)) return false;
|
|
16601
|
+
if (type === "session_start") sawSessionStart = true;
|
|
16602
|
+
}
|
|
16603
|
+
} catch {
|
|
16604
|
+
return false;
|
|
16605
|
+
} finally {
|
|
16606
|
+
lines.close();
|
|
16607
|
+
input.destroy();
|
|
16608
|
+
}
|
|
16609
|
+
return sawSessionStart;
|
|
16610
|
+
}
|
|
16611
|
+
|
|
16612
|
+
// src/storage/session-store/summary-builder.ts
|
|
16613
|
+
import { createReadStream as createReadStream4 } from "node:fs";
|
|
16614
|
+
import { createInterface as createInterface4 } from "node:readline";
|
|
16532
16615
|
async function summarizeSessionFile(opts) {
|
|
16533
16616
|
return summarizeSessionEventSequence({
|
|
16534
16617
|
id: opts.id,
|
|
@@ -16645,8 +16728,8 @@ async function summarizeSessionEventSequence(opts) {
|
|
|
16645
16728
|
}
|
|
16646
16729
|
}
|
|
16647
16730
|
async function* iterateSessionEvents(file, secretScrubber) {
|
|
16648
|
-
const stream =
|
|
16649
|
-
const lines =
|
|
16731
|
+
const stream = createReadStream4(file, { encoding: "utf8" });
|
|
16732
|
+
const lines = createInterface4({ input: stream, crlfDelay: Infinity });
|
|
16650
16733
|
try {
|
|
16651
16734
|
for await (const line of lines) {
|
|
16652
16735
|
if (!line.trim()) continue;
|
|
@@ -17542,6 +17625,10 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
17542
17625
|
await deleteSessionArtifacts({ rootDir: this.dir, id, jsonlPath });
|
|
17543
17626
|
await this.writeTombstone(id);
|
|
17544
17627
|
}
|
|
17628
|
+
async isEmpty(id) {
|
|
17629
|
+
const canonicalId = await this.resolveId(id);
|
|
17630
|
+
return isStrictlyEmptySessionFile(this.sessionPath(canonicalId, ".jsonl"));
|
|
17631
|
+
}
|
|
17545
17632
|
async delete(id) {
|
|
17546
17633
|
if (this.catalogClient) {
|
|
17547
17634
|
const canonical = await this.resolveId(id);
|
|
@@ -21471,6 +21558,12 @@ function findExchangeStart(messages, userIndex) {
|
|
|
21471
21558
|
|
|
21472
21559
|
// src/execution/auto-compaction-middleware.ts
|
|
21473
21560
|
var LEVEL_RANK = { warn: 0, soft: 1, hard: 2 };
|
|
21561
|
+
function pressureLevelFor(load, thresholds) {
|
|
21562
|
+
if (load >= thresholds.hard) return "hard";
|
|
21563
|
+
if (load >= thresholds.soft) return "soft";
|
|
21564
|
+
if (load >= thresholds.warn) return "warn";
|
|
21565
|
+
return null;
|
|
21566
|
+
}
|
|
21474
21567
|
var MAX_DIGEST_LOG_CHARS = 4e3;
|
|
21475
21568
|
function truncateDigest(digest) {
|
|
21476
21569
|
if (digest.length <= MAX_DIGEST_LOG_CHARS) return digest;
|
|
@@ -21516,8 +21609,19 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
21516
21609
|
* 1 / 2.5 = 0.4.
|
|
21517
21610
|
*/
|
|
21518
21611
|
static GUARD_GATE_LOAD = 0.4;
|
|
21612
|
+
/**
|
|
21613
|
+
* How much the context must grow between two history-rewriting hygiene
|
|
21614
|
+
* passes, as a fraction of the available input window and as an absolute
|
|
21615
|
+
* floor. Every pass rewrites already-transmitted messages, which forces the
|
|
21616
|
+
* provider to re-cache the whole prompt; spacing the passes out is what lets
|
|
21617
|
+
* the conversation prefix stay cached for the turns in between.
|
|
21618
|
+
*/
|
|
21619
|
+
static HYGIENE_GROWTH_RATIO = 0.15;
|
|
21620
|
+
static HYGIENE_MIN_GROWTH_TOKENS = 2e4;
|
|
21519
21621
|
/** Tracks the most recent no-op attempt so we can avoid re-firing per turn. */
|
|
21520
21622
|
lastNoopAttempt = null;
|
|
21623
|
+
/** Context size at the last hygiene pass; anchors the growth interval. */
|
|
21624
|
+
lastHygieneTokens = null;
|
|
21521
21625
|
/**
|
|
21522
21626
|
* Cached token estimate from the last handler() invocation. When the
|
|
21523
21627
|
* message count and tool count haven't changed since the last estimate
|
|
@@ -21577,55 +21681,9 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
21577
21681
|
handler() {
|
|
21578
21682
|
return async (ctx, next) => {
|
|
21579
21683
|
if (!this._enabled) return next(ctx);
|
|
21580
|
-
|
|
21581
|
-
maxRetainedTokens: this.resolveToolResultRetention(ctx)
|
|
21582
|
-
});
|
|
21583
|
-
const receiptHygiene = collapseAcknowledgedToolReceipts(rawHygiene.messages, {
|
|
21584
|
-
maxPairs: this.resolveToolReceiptRetention(ctx)
|
|
21585
|
-
});
|
|
21586
|
-
if (rawHygiene.changed || receiptHygiene.changed) {
|
|
21587
|
-
ctx.state.replaceMessages(receiptHygiene.messages);
|
|
21588
|
-
ctx.clearFileTracking();
|
|
21589
|
-
this.invalidateTokenCaches(ctx);
|
|
21590
|
-
}
|
|
21591
|
-
const msgCount = ctx.messages.length;
|
|
21592
|
-
const toolCount = (ctx.tools ?? []).length;
|
|
21593
|
-
const revision = ctx.state?.revision ?? -1;
|
|
21594
|
-
let tokens;
|
|
21595
|
-
const anchorAt = typeof ctx.meta?.["realAnchorMsgCount"] === "number" ? ctx.meta["realAnchorMsgCount"] : void 0;
|
|
21596
|
-
const anchored = realAnchoredInputTokens(ctx.messages, ctx.lastRealInputTokens, anchorAt);
|
|
21597
|
-
if (anchored !== null) {
|
|
21598
|
-
tokens = anchored;
|
|
21599
|
-
} else if (this._estimator) {
|
|
21600
|
-
tokens = this._estimator(ctx);
|
|
21601
|
-
} else if (msgCount === this._cachedMsgCount && toolCount === this._cachedToolCount && revision === this._cachedRevision && ctx.systemPrompt === this._cachedSystemRef && ctx.tools === this._cachedToolsRef && this._cachedTokens >= 0) {
|
|
21602
|
-
tokens = this._cachedTokens;
|
|
21603
|
-
} else if (this.tryStashedTokens(ctx, msgCount, toolCount, revision) !== null) {
|
|
21604
|
-
const stashed = this.tryStashedTokens(ctx, msgCount, toolCount, revision);
|
|
21605
|
-
const cal = getCalibrationState(`${ctx.provider?.id ?? "unknown"}/${ctx.model}`);
|
|
21606
|
-
tokens = cal.calibrated ? Math.round(stashed * Math.min(1.5, Math.max(0.5, cal.ratio))) : stashed;
|
|
21607
|
-
this._cachedTokens = tokens;
|
|
21608
|
-
this._cachedMsgCount = msgCount;
|
|
21609
|
-
this._cachedToolCount = toolCount;
|
|
21610
|
-
this._cachedRevision = revision;
|
|
21611
|
-
this._cachedSystemRef = ctx.systemPrompt;
|
|
21612
|
-
this._cachedToolsRef = ctx.tools;
|
|
21613
|
-
} else {
|
|
21614
|
-
tokens = estimateRequestTokensCalibrated(
|
|
21615
|
-
ctx.messages,
|
|
21616
|
-
ctx.systemPrompt,
|
|
21617
|
-
ctx.tools ?? [],
|
|
21618
|
-
`${ctx.provider?.id ?? "unknown"}/${ctx.model}`
|
|
21619
|
-
).total;
|
|
21620
|
-
this._cachedTokens = tokens;
|
|
21621
|
-
this._cachedMsgCount = msgCount;
|
|
21622
|
-
this._cachedToolCount = toolCount;
|
|
21623
|
-
this._cachedRevision = revision;
|
|
21624
|
-
this._cachedSystemRef = ctx.systemPrompt;
|
|
21625
|
-
this._cachedToolsRef = ctx.tools;
|
|
21626
|
-
}
|
|
21684
|
+
let tokens = this.estimateContextTokens(ctx);
|
|
21627
21685
|
const runtimeMaxContext = effectiveMaxContext(ctx, this._maxContext);
|
|
21628
|
-
|
|
21686
|
+
let budget = computeContextWindowBudget(ctx, tokens, runtimeMaxContext);
|
|
21629
21687
|
const calibratedLoad = budget.load;
|
|
21630
21688
|
const policy = this.policyProvider?.(ctx);
|
|
21631
21689
|
const thresholds = policy?.thresholds ?? {
|
|
@@ -21639,22 +21697,27 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
21639
21697
|
});
|
|
21640
21698
|
const aggressiveOn = policy?.aggressiveOn ?? this.aggressiveOn;
|
|
21641
21699
|
const targetLoad = normalizeTargetLoad(policy?.targetLoad, adaptiveThresholds);
|
|
21642
|
-
let load = calibratedLoad;
|
|
21643
|
-
|
|
21644
|
-
const guardTotal = estimateRequestTokensUpperBound(
|
|
21645
|
-
ctx.messages,
|
|
21646
|
-
ctx.systemPrompt,
|
|
21647
|
-
ctx.tools ?? [],
|
|
21648
|
-
`${ctx.provider?.id ?? "unknown"}/${ctx.model}`
|
|
21649
|
-
).total;
|
|
21650
|
-
const guardLoad = guardTotal / budget.availableInputTokens;
|
|
21651
|
-
if (guardLoad > load) load = guardLoad;
|
|
21652
|
-
}
|
|
21653
|
-
const level = load >= adaptiveThresholds.hard ? "hard" : load >= adaptiveThresholds.soft ? "soft" : load >= adaptiveThresholds.warn ? "warn" : null;
|
|
21700
|
+
let load = this.applySendGuard(ctx, calibratedLoad, budget.availableInputTokens);
|
|
21701
|
+
let level = pressureLevelFor(load, adaptiveThresholds);
|
|
21654
21702
|
if (!level) {
|
|
21655
21703
|
this.lastNoopAttempt = null;
|
|
21656
21704
|
return next(ctx);
|
|
21657
21705
|
}
|
|
21706
|
+
if (this.shouldRunHygiene(level, tokens, budget.availableInputTokens)) {
|
|
21707
|
+
const changed = this.runHistoryHygiene(ctx);
|
|
21708
|
+
tokens = changed ? this.estimateContextTokens(ctx) : tokens;
|
|
21709
|
+
this.lastHygieneTokens = tokens;
|
|
21710
|
+
if (changed) {
|
|
21711
|
+
budget = computeContextWindowBudget(ctx, tokens, runtimeMaxContext);
|
|
21712
|
+
load = this.applySendGuard(ctx, budget.load, budget.availableInputTokens);
|
|
21713
|
+
const relevelled = pressureLevelFor(load, adaptiveThresholds);
|
|
21714
|
+
if (!relevelled) {
|
|
21715
|
+
this.lastNoopAttempt = null;
|
|
21716
|
+
return next(ctx);
|
|
21717
|
+
}
|
|
21718
|
+
level = relevelled;
|
|
21719
|
+
}
|
|
21720
|
+
}
|
|
21658
21721
|
if (this.shouldSkipNoopRetry(level, tokens)) {
|
|
21659
21722
|
return next(ctx);
|
|
21660
21723
|
}
|
|
@@ -21671,6 +21734,123 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
21671
21734
|
return next(ctx);
|
|
21672
21735
|
};
|
|
21673
21736
|
}
|
|
21737
|
+
/**
|
|
21738
|
+
* Full-request token total for the current context.
|
|
21739
|
+
*
|
|
21740
|
+
* Reuses the last estimate when the context hasn't grown since the previous
|
|
21741
|
+
* check — common in autonomous idle loops. The cached value is invalidated
|
|
21742
|
+
* whenever messages or tools change.
|
|
21743
|
+
*
|
|
21744
|
+
* IMPORTANT: the cache is only valid for the deterministic
|
|
21745
|
+
* `estimateRequestTokensCalibrated` path (messages+system+tools → fixed
|
|
21746
|
+
* output). When a custom `_estimator` is provided (e.g. in tests with a
|
|
21747
|
+
* mutable closure, or a dynamic policy provider), always call it fresh — the
|
|
21748
|
+
* estimator owns its own semantics and the middleware cannot safely cache its
|
|
21749
|
+
* result across calls.
|
|
21750
|
+
*/
|
|
21751
|
+
estimateContextTokens(ctx) {
|
|
21752
|
+
const msgCount = ctx.messages.length;
|
|
21753
|
+
const toolCount = (ctx.tools ?? []).length;
|
|
21754
|
+
const revision = ctx.state?.revision ?? -1;
|
|
21755
|
+
const anchorAt = typeof ctx.meta?.["realAnchorMsgCount"] === "number" ? ctx.meta["realAnchorMsgCount"] : void 0;
|
|
21756
|
+
const anchored = realAnchoredInputTokens(ctx.messages, ctx.lastRealInputTokens, anchorAt);
|
|
21757
|
+
if (anchored !== null) return anchored;
|
|
21758
|
+
if (this._estimator) return this._estimator(ctx);
|
|
21759
|
+
if (msgCount === this._cachedMsgCount && toolCount === this._cachedToolCount && revision === this._cachedRevision && ctx.systemPrompt === this._cachedSystemRef && ctx.tools === this._cachedToolsRef && this._cachedTokens >= 0) {
|
|
21760
|
+
return this._cachedTokens;
|
|
21761
|
+
}
|
|
21762
|
+
const stashed = this.tryStashedTokens(ctx, msgCount, toolCount, revision);
|
|
21763
|
+
let tokens;
|
|
21764
|
+
if (stashed !== null) {
|
|
21765
|
+
const cal = getCalibrationState(`${ctx.provider?.id ?? "unknown"}/${ctx.model}`);
|
|
21766
|
+
tokens = cal.calibrated ? Math.round(stashed * Math.min(1.5, Math.max(0.5, cal.ratio))) : stashed;
|
|
21767
|
+
} else {
|
|
21768
|
+
tokens = estimateRequestTokensCalibrated(
|
|
21769
|
+
ctx.messages,
|
|
21770
|
+
ctx.systemPrompt,
|
|
21771
|
+
ctx.tools ?? [],
|
|
21772
|
+
`${ctx.provider?.id ?? "unknown"}/${ctx.model}`
|
|
21773
|
+
).total;
|
|
21774
|
+
}
|
|
21775
|
+
this._cachedTokens = tokens;
|
|
21776
|
+
this._cachedMsgCount = msgCount;
|
|
21777
|
+
this._cachedToolCount = toolCount;
|
|
21778
|
+
this._cachedRevision = revision;
|
|
21779
|
+
this._cachedSystemRef = ctx.systemPrompt;
|
|
21780
|
+
this._cachedToolsRef = ctx.tools;
|
|
21781
|
+
return tokens;
|
|
21782
|
+
}
|
|
21783
|
+
/**
|
|
21784
|
+
* Never-undercount send guard.
|
|
21785
|
+
*
|
|
21786
|
+
* The calibrated estimate can under-count dense content (CJK, base64,
|
|
21787
|
+
* minified) by >1.5×, which would let an over-limit request slip past the
|
|
21788
|
+
* thresholds and reach the provider. Once the calibrated load is high enough
|
|
21789
|
+
* that even the max density factor (2.5×) *could* overflow (load ≥ 1/2.5 =
|
|
21790
|
+
* 0.4), re-check with the upper-bound estimator and escalate to whichever
|
|
21791
|
+
* load is larger. Below 0.4 an overflow is arithmetically impossible, so the
|
|
21792
|
+
* extra scan is skipped.
|
|
21793
|
+
*/
|
|
21794
|
+
applySendGuard(ctx, calibratedLoad, availableInputTokens) {
|
|
21795
|
+
if (calibratedLoad < _AutoCompactionMiddleware.GUARD_GATE_LOAD) return calibratedLoad;
|
|
21796
|
+
const guardTotal = estimateRequestTokensUpperBound(
|
|
21797
|
+
ctx.messages,
|
|
21798
|
+
ctx.systemPrompt,
|
|
21799
|
+
ctx.tools ?? [],
|
|
21800
|
+
`${ctx.provider?.id ?? "unknown"}/${ctx.model}`
|
|
21801
|
+
).total;
|
|
21802
|
+
const guardLoad = guardTotal / availableInputTokens;
|
|
21803
|
+
return guardLoad > calibratedLoad ? guardLoad : calibratedLoad;
|
|
21804
|
+
}
|
|
21805
|
+
/**
|
|
21806
|
+
* Rewrite acknowledged tool protocol in place.
|
|
21807
|
+
*
|
|
21808
|
+
* Tool results are protocol inputs for the immediately following model
|
|
21809
|
+
* response, not unlimited durable prompt memory. Once a later assistant
|
|
21810
|
+
* message proves the provider consumed them, keep a mode-sized raw window and
|
|
21811
|
+
* replace the rest with semantic head/tail receipts, then fold fully-aged
|
|
21812
|
+
* pairs into the history digest.
|
|
21813
|
+
*
|
|
21814
|
+
* Every one of those edits touches a message the provider has already seen,
|
|
21815
|
+
* so each call costs a full prompt re-cache — it belongs behind
|
|
21816
|
+
* {@link shouldRunHygiene}, never on the per-turn path. Content staleness is
|
|
21817
|
+
* not a reason to bypass that gate: the edit/replace tools guard against
|
|
21818
|
+
* acting on an out-of-date read with their own mtime + sha-256 check.
|
|
21819
|
+
*
|
|
21820
|
+
* @returns whether the conversation was rewritten.
|
|
21821
|
+
*/
|
|
21822
|
+
runHistoryHygiene(ctx) {
|
|
21823
|
+
const raw = eliseAcknowledgedToolResults(ctx.messages, {
|
|
21824
|
+
maxRetainedTokens: this.resolveToolResultRetention(ctx)
|
|
21825
|
+
});
|
|
21826
|
+
const receipts = collapseAcknowledgedToolReceipts(raw.messages, {
|
|
21827
|
+
maxPairs: this.resolveToolReceiptRetention(ctx)
|
|
21828
|
+
});
|
|
21829
|
+
if (!raw.changed && !receipts.changed) return false;
|
|
21830
|
+
ctx.state.replaceMessages(receipts.messages);
|
|
21831
|
+
ctx.clearFileTracking();
|
|
21832
|
+
this.invalidateTokenCaches(ctx);
|
|
21833
|
+
return true;
|
|
21834
|
+
}
|
|
21835
|
+
/**
|
|
21836
|
+
* Whether the history-rewriting hygiene pass may run this turn.
|
|
21837
|
+
*
|
|
21838
|
+
* Hard pressure always runs it — staying under the window outranks caching.
|
|
21839
|
+
* Otherwise it runs at most once per growth interval, so the conversation
|
|
21840
|
+
* stays append-only (and therefore cacheable by the provider) in between.
|
|
21841
|
+
*/
|
|
21842
|
+
shouldRunHygiene(level, tokens, availableInputTokens) {
|
|
21843
|
+
if (level === "hard") return true;
|
|
21844
|
+
const last = this.lastHygieneTokens;
|
|
21845
|
+
if (last === null) return true;
|
|
21846
|
+
const anchor = Math.min(last, tokens);
|
|
21847
|
+
this.lastHygieneTokens = anchor;
|
|
21848
|
+
const interval = Math.max(
|
|
21849
|
+
_AutoCompactionMiddleware.HYGIENE_MIN_GROWTH_TOKENS,
|
|
21850
|
+
Math.floor(availableInputTokens * _AutoCompactionMiddleware.HYGIENE_GROWTH_RATIO)
|
|
21851
|
+
);
|
|
21852
|
+
return tokens - anchor >= interval;
|
|
21853
|
+
}
|
|
21674
21854
|
/**
|
|
21675
21855
|
* H1: try to read a pre-computed token total from `ctx.lastRequestTokens`
|
|
21676
21856
|
* (set by the agent loop's pre-flight or its restash in emitContextPct).
|
|
@@ -26767,26 +26947,29 @@ var DefaultProviderRunner = class {
|
|
|
26767
26947
|
// src/execution/retry-policy.ts
|
|
26768
26948
|
import { randomInt } from "node:crypto";
|
|
26769
26949
|
var MAX_RETRY_AFTER_MS = 6e4;
|
|
26950
|
+
var MODEL_RETRIES = 3;
|
|
26770
26951
|
var MAX_ATTEMPTS_BY_KIND = {
|
|
26771
|
-
rate_limit:
|
|
26952
|
+
rate_limit: MODEL_RETRIES,
|
|
26953
|
+
overloaded: MODEL_RETRIES,
|
|
26954
|
+
server: MODEL_RETRIES,
|
|
26955
|
+
timeout: MODEL_RETRIES,
|
|
26956
|
+
network: MODEL_RETRIES,
|
|
26957
|
+
stream_hang: MODEL_RETRIES,
|
|
26772
26958
|
quota_exhausted: 0,
|
|
26773
|
-
stream_hang: 2,
|
|
26774
|
-
// proxy-level timeout — retrying 5x wastes ~40s before fallback kicks in
|
|
26775
|
-
overloaded: 3,
|
|
26776
|
-
server: 3,
|
|
26777
|
-
timeout: 2,
|
|
26778
|
-
network: 2,
|
|
26779
26959
|
auth: 0,
|
|
26780
26960
|
invalid_request: 0,
|
|
26781
26961
|
context_overflow: 0,
|
|
26782
26962
|
content_filter: 0,
|
|
26783
26963
|
unknown: 0
|
|
26784
26964
|
};
|
|
26965
|
+
var FAILOVER_RETRY_AFTER_MS = 15e3;
|
|
26785
26966
|
var DefaultRetryPolicy = class {
|
|
26786
26967
|
shouldRetry(err, attempt) {
|
|
26787
26968
|
const isProviderErr = err instanceof ProviderError || ProviderError.isProviderError(err);
|
|
26788
26969
|
if (isProviderErr) {
|
|
26789
26970
|
if (!err.retryable) return false;
|
|
26971
|
+
const hint = retryAfterMsFromError(err);
|
|
26972
|
+
if (hint !== void 0 && hint >= FAILOVER_RETRY_AFTER_MS) return false;
|
|
26790
26973
|
return attempt < this.maxAttempts(err);
|
|
26791
26974
|
}
|
|
26792
26975
|
const msg = err.message ?? "";
|
|
@@ -32074,7 +32257,7 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
|
|
|
32074
32257
|
};
|
|
32075
32258
|
|
|
32076
32259
|
// src/security/permission-helpers.ts
|
|
32077
|
-
import { realpathSync } from "node:fs";
|
|
32260
|
+
import { realpathSync as realpathSync2 } from "node:fs";
|
|
32078
32261
|
import * as path38 from "node:path";
|
|
32079
32262
|
function matchesTrust(patterns, subject) {
|
|
32080
32263
|
return patterns.includes(subject) || matchAny(patterns, subject);
|
|
@@ -32159,7 +32342,7 @@ function realpathOfNearestExisting(p) {
|
|
|
32159
32342
|
const tail = [];
|
|
32160
32343
|
for (; ; ) {
|
|
32161
32344
|
try {
|
|
32162
|
-
return tail.length === 0 ?
|
|
32345
|
+
return tail.length === 0 ? realpathSync2(probe) : path38.join(realpathSync2(probe), ...tail);
|
|
32163
32346
|
} catch {
|
|
32164
32347
|
const parent = path38.dirname(probe);
|
|
32165
32348
|
if (parent === probe) return p;
|
|
@@ -33922,6 +34105,10 @@ var IN_PROJECT_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
|
33922
34105
|
"fallbackModels",
|
|
33923
34106
|
"fallbackBridge",
|
|
33924
34107
|
"fallbackProfiles",
|
|
34108
|
+
// The profile SELECTOR. No broader than its siblings: a repo that can write
|
|
34109
|
+
// `fallbackModels` and `fallbackProfiles` already controls the chain outright,
|
|
34110
|
+
// and this one can only name a profile the user already defined.
|
|
34111
|
+
"fallbackProfile",
|
|
33925
34112
|
"favoriteModels",
|
|
33926
34113
|
"favoriteModelsOnly",
|
|
33927
34114
|
"modelAvailabilitySchedule",
|
|
@@ -33969,6 +34156,10 @@ var KNOWN_DENIED_IN_PROJECT = [
|
|
|
33969
34156
|
{
|
|
33970
34157
|
key: "git",
|
|
33971
34158
|
reason: "Carries git.identity (GIT_AUTHOR_*/GIT_COMMITTER_* injection): a repo-committed config could spoof the author identity written into the victim's commit history (impersonation)."
|
|
34159
|
+
},
|
|
34160
|
+
{
|
|
34161
|
+
key: "fallbackMaxLastResortCandidates",
|
|
34162
|
+
reason: "Bounds how many of the user's OTHER configured providers may be swept in as last-resort failover. Setting it to 0 from a repo-committed config would silently strip that depth during an outage. It was already stripped in practice (absent from the allow-list) but was missing from the key registry, so this gate never checked it."
|
|
33972
34163
|
}
|
|
33973
34164
|
];
|
|
33974
34165
|
var KNOWN_CONFIG_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -33989,11 +34180,13 @@ var KNOWN_CONFIG_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
|
|
|
33989
34180
|
"fallbackModels",
|
|
33990
34181
|
"fallbackBridge",
|
|
33991
34182
|
"fallbackProfiles",
|
|
34183
|
+
"fallbackProfile",
|
|
33992
34184
|
"favoriteModels",
|
|
33993
34185
|
"favoriteModelsOnly",
|
|
33994
34186
|
"modelAvailabilitySchedule",
|
|
33995
34187
|
"fallbackAuto",
|
|
33996
34188
|
"fallbackStickiness",
|
|
34189
|
+
"fallbackMaxLastResortCandidates",
|
|
33997
34190
|
"hooks",
|
|
33998
34191
|
"plugins",
|
|
33999
34192
|
"pluginManager",
|
|
@@ -34079,6 +34272,17 @@ var IN_PROJECT_DENIED_PATHS = [
|
|
|
34079
34272
|
// operator owns, not the checked-out repository.
|
|
34080
34273
|
path: "tools.kanbanGovernance",
|
|
34081
34274
|
reason: "Repo-committed config could disable a Kanban governance gate the operator switched on, letting product mutations run outside any managed card."
|
|
34275
|
+
},
|
|
34276
|
+
{
|
|
34277
|
+
// The bridge spawn path resolves the CLI entry by walking UP from the
|
|
34278
|
+
// project root, so a repo that ships its own `packages/cli/dist/index.js`
|
|
34279
|
+
// gets that file spawned with `process.execPath` on WebUI boot — no
|
|
34280
|
+
// prompt, no banner. Turning the feature on is therefore equivalent to
|
|
34281
|
+
// arbitrary code execution for a hostile checkout, which makes this an
|
|
34282
|
+
// operator-owned switch and never a repo-owned one.
|
|
34283
|
+
// See discover-mailbox-bridge.ts:findWorkspaceCliEntry.
|
|
34284
|
+
path: "features.mailboxBridge",
|
|
34285
|
+
reason: "Enables the mailbox bridge, whose CLI-entry resolution walks up from the project root \u2014 a repo-supplied packages/cli/dist/index.js would be spawned on WebUI boot."
|
|
34082
34286
|
}
|
|
34083
34287
|
];
|
|
34084
34288
|
function deleteNestedPath(target, path45) {
|
|
@@ -78,8 +78,19 @@ export declare class AutoCompactionMiddleware {
|
|
|
78
78
|
* 1 / 2.5 = 0.4.
|
|
79
79
|
*/
|
|
80
80
|
private static readonly GUARD_GATE_LOAD;
|
|
81
|
+
/**
|
|
82
|
+
* How much the context must grow between two history-rewriting hygiene
|
|
83
|
+
* passes, as a fraction of the available input window and as an absolute
|
|
84
|
+
* floor. Every pass rewrites already-transmitted messages, which forces the
|
|
85
|
+
* provider to re-cache the whole prompt; spacing the passes out is what lets
|
|
86
|
+
* the conversation prefix stay cached for the turns in between.
|
|
87
|
+
*/
|
|
88
|
+
private static readonly HYGIENE_GROWTH_RATIO;
|
|
89
|
+
private static readonly HYGIENE_MIN_GROWTH_TOKENS;
|
|
81
90
|
/** Tracks the most recent no-op attempt so we can avoid re-firing per turn. */
|
|
82
91
|
private lastNoopAttempt;
|
|
92
|
+
/** Context size at the last hygiene pass; anchors the growth interval. */
|
|
93
|
+
private lastHygieneTokens;
|
|
83
94
|
/**
|
|
84
95
|
* Cached token estimate from the last handler() invocation. When the
|
|
85
96
|
* message count and tool count haven't changed since the last estimate
|
|
@@ -128,6 +139,59 @@ export declare class AutoCompactionMiddleware {
|
|
|
128
139
|
* tokens or compacting. */
|
|
129
140
|
setEnabled(enabled: boolean): void;
|
|
130
141
|
handler(): MiddlewareHandler<Context>;
|
|
142
|
+
/**
|
|
143
|
+
* Full-request token total for the current context.
|
|
144
|
+
*
|
|
145
|
+
* Reuses the last estimate when the context hasn't grown since the previous
|
|
146
|
+
* check — common in autonomous idle loops. The cached value is invalidated
|
|
147
|
+
* whenever messages or tools change.
|
|
148
|
+
*
|
|
149
|
+
* IMPORTANT: the cache is only valid for the deterministic
|
|
150
|
+
* `estimateRequestTokensCalibrated` path (messages+system+tools → fixed
|
|
151
|
+
* output). When a custom `_estimator` is provided (e.g. in tests with a
|
|
152
|
+
* mutable closure, or a dynamic policy provider), always call it fresh — the
|
|
153
|
+
* estimator owns its own semantics and the middleware cannot safely cache its
|
|
154
|
+
* result across calls.
|
|
155
|
+
*/
|
|
156
|
+
private estimateContextTokens;
|
|
157
|
+
/**
|
|
158
|
+
* Never-undercount send guard.
|
|
159
|
+
*
|
|
160
|
+
* The calibrated estimate can under-count dense content (CJK, base64,
|
|
161
|
+
* minified) by >1.5×, which would let an over-limit request slip past the
|
|
162
|
+
* thresholds and reach the provider. Once the calibrated load is high enough
|
|
163
|
+
* that even the max density factor (2.5×) *could* overflow (load ≥ 1/2.5 =
|
|
164
|
+
* 0.4), re-check with the upper-bound estimator and escalate to whichever
|
|
165
|
+
* load is larger. Below 0.4 an overflow is arithmetically impossible, so the
|
|
166
|
+
* extra scan is skipped.
|
|
167
|
+
*/
|
|
168
|
+
private applySendGuard;
|
|
169
|
+
/**
|
|
170
|
+
* Rewrite acknowledged tool protocol in place.
|
|
171
|
+
*
|
|
172
|
+
* Tool results are protocol inputs for the immediately following model
|
|
173
|
+
* response, not unlimited durable prompt memory. Once a later assistant
|
|
174
|
+
* message proves the provider consumed them, keep a mode-sized raw window and
|
|
175
|
+
* replace the rest with semantic head/tail receipts, then fold fully-aged
|
|
176
|
+
* pairs into the history digest.
|
|
177
|
+
*
|
|
178
|
+
* Every one of those edits touches a message the provider has already seen,
|
|
179
|
+
* so each call costs a full prompt re-cache — it belongs behind
|
|
180
|
+
* {@link shouldRunHygiene}, never on the per-turn path. Content staleness is
|
|
181
|
+
* not a reason to bypass that gate: the edit/replace tools guard against
|
|
182
|
+
* acting on an out-of-date read with their own mtime + sha-256 check.
|
|
183
|
+
*
|
|
184
|
+
* @returns whether the conversation was rewritten.
|
|
185
|
+
*/
|
|
186
|
+
private runHistoryHygiene;
|
|
187
|
+
/**
|
|
188
|
+
* Whether the history-rewriting hygiene pass may run this turn.
|
|
189
|
+
*
|
|
190
|
+
* Hard pressure always runs it — staying under the window outranks caching.
|
|
191
|
+
* Otherwise it runs at most once per growth interval, so the conversation
|
|
192
|
+
* stays append-only (and therefore cacheable by the provider) in between.
|
|
193
|
+
*/
|
|
194
|
+
private shouldRunHygiene;
|
|
131
195
|
/**
|
|
132
196
|
* H1: try to read a pre-computed token total from `ctx.lastRequestTokens`
|
|
133
197
|
* (set by the agent loop's pre-flight or its restash in emitContextPct).
|
|
@@ -63,6 +63,10 @@ export interface AcknowledgedToolReceiptCollapse extends EliseResult {
|
|
|
63
63
|
* become semantic receipts while their exact payload remains in the session
|
|
64
64
|
* log. Tool ids and provider metadata stay intact so strict provider replay
|
|
65
65
|
* adjacency remains valid.
|
|
66
|
+
*
|
|
67
|
+
* Every edit here rewrites a message the provider has already seen, so callers
|
|
68
|
+
* must run this on a pressure-gated interval rather than per turn — see
|
|
69
|
+
* `AutoCompactionMiddleware.shouldRunHygiene`.
|
|
66
70
|
*/
|
|
67
71
|
export declare function eliseAcknowledgedToolResults(messages: readonly Message[], opts: {
|
|
68
72
|
maxRetainedTokens: number;
|
|
@@ -25,7 +25,7 @@ export { OneShotOrchestrator } from './one-shot-llm.js';
|
|
|
25
25
|
export { type ParallelEngineState, ParallelEternalEngine, type ParallelEternalOptions, type ParallelIterationStage, } from './parallel-eternal-engine.js';
|
|
26
26
|
export { buildRefinerContextSections, DEFAULT_REFINER_RETRY_FEEDBACK, type EnhanceFailureKind, enhanceUserPrompt, gatedEnhancerReasoning, isValidEnglishRefinement, normalizedEqual, parseBilingualEnhancement, recentTextTurns, shouldEnhance, } from './prompt-enhancer.js';
|
|
27
27
|
export { DefaultPromptLoader, type PromptLoaderOptions, renderPrompt } from './prompt-loader.js';
|
|
28
|
-
export { DefaultRetryPolicy } from './retry-policy.js';
|
|
28
|
+
export { DefaultRetryPolicy, MODEL_RETRIES } from './retry-policy.js';
|
|
29
29
|
export { SelectiveCompactor, type SelectiveCompactorOptions } from './selective-compactor.js';
|
|
30
30
|
export { DefaultSkillLoader, type SkillLoaderOptions } from './skill-loader.js';
|
|
31
31
|
export { type CompactorStrategy, createStrategyCompactor, type StrategyCompactorOptions, } from './strategy-compactor.js';
|