@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/tools/index.js
CHANGED
|
@@ -2024,6 +2024,12 @@ function normalizeModelRef(ref, defaultProvider) {
|
|
|
2024
2024
|
function hasText(value) {
|
|
2025
2025
|
return typeof value === "string" && value.trim().length > 0;
|
|
2026
2026
|
}
|
|
2027
|
+
function asRefList(value) {
|
|
2028
|
+
return Array.isArray(value) ? value : void 0;
|
|
2029
|
+
}
|
|
2030
|
+
function asProfileName(value) {
|
|
2031
|
+
return hasText(value) ? value : void 0;
|
|
2032
|
+
}
|
|
2027
2033
|
function providerHasKey(entry) {
|
|
2028
2034
|
if (!entry) return false;
|
|
2029
2035
|
if (hasText(entry.apiKey)) return true;
|
|
@@ -2034,7 +2040,7 @@ function providerHasKey(entry) {
|
|
|
2034
2040
|
}
|
|
2035
2041
|
function visibleProviderModels(config, providerId, providerModels) {
|
|
2036
2042
|
const entry = config.providers?.[providerId];
|
|
2037
|
-
return entry?.models
|
|
2043
|
+
return Array.isArray(entry?.models) ? [...entry.models] : providerModels;
|
|
2038
2044
|
}
|
|
2039
2045
|
function buildProfiles(config) {
|
|
2040
2046
|
const entries = /* @__PURE__ */ new Map();
|
|
@@ -2071,13 +2077,34 @@ var FallbackProfileManager = class {
|
|
|
2071
2077
|
listProfiles() {
|
|
2072
2078
|
return Object.freeze([...this.profiles.keys()]);
|
|
2073
2079
|
}
|
|
2080
|
+
/**
|
|
2081
|
+
* The profile the session has selected (`config.fallbackProfile`, set by
|
|
2082
|
+
* `/fallback profile use <name>`), or undefined when none is selected or the
|
|
2083
|
+
* name no longer resolves to a defined profile.
|
|
2084
|
+
*
|
|
2085
|
+
* Consulted by every resolution entry point when the caller does not name a
|
|
2086
|
+
* profile itself. Without this the leader — which passes no profile — could
|
|
2087
|
+
* never use a named profile at all: `config.fallbackProfiles` was reachable
|
|
2088
|
+
* only by copying a chain into `fallbackModels`.
|
|
2089
|
+
*/
|
|
2090
|
+
activeProfileName() {
|
|
2091
|
+
const name = asProfileName(this.config.fallbackProfile);
|
|
2092
|
+
return name && this.profiles.has(name) ? name : void 0;
|
|
2093
|
+
}
|
|
2074
2094
|
// ── Resolution ─────────────────────────────────────────────────────────
|
|
2075
2095
|
/**
|
|
2076
2096
|
* Resolve a named fallback profile to a validated, provider-filtered chain.
|
|
2077
2097
|
*
|
|
2078
|
-
* Returns an empty chain when
|
|
2079
|
-
*
|
|
2080
|
-
*
|
|
2098
|
+
* Returns an empty chain when the profile doesn't exist, or when every entry
|
|
2099
|
+
* is excluded, quarantined, or blacked out.
|
|
2100
|
+
*
|
|
2101
|
+
* Filtering is intentionally identical to {@link resolveRefs} (the explicit
|
|
2102
|
+
* `fallbackModels` path): the self-exclusion, the runtime status tracker,
|
|
2103
|
+
* and the availability calendar — nothing else. Anything a named profile
|
|
2104
|
+
* drops, an explicit chain drops too, and vice versa. Profiles used to apply
|
|
2105
|
+
* two extra filters (provider "usability" and the `providers[].models`
|
|
2106
|
+
* snapshot) that the explicit path did not, which silently rerouted roles
|
|
2107
|
+
* pinned to a profile onto a different model than the one configured.
|
|
2081
2108
|
*
|
|
2082
2109
|
* @param name - Profile name from config.fallbackProfiles.
|
|
2083
2110
|
* @param defaultProvider - Used when an entry has no explicit provider.
|
|
@@ -2098,13 +2125,9 @@ var FallbackProfileManager = class {
|
|
|
2098
2125
|
if (seen.has(key)) continue;
|
|
2099
2126
|
seen.add(key);
|
|
2100
2127
|
if (excludeKey && key === excludeKey) continue;
|
|
2101
|
-
const health = this.checkProvider(providerId);
|
|
2102
|
-
if (!health.usable) continue;
|
|
2103
2128
|
if (this.statusTracker && !this.statusTracker.isAvailable(providerId, parsed.model)) continue;
|
|
2104
2129
|
if (!evaluateModelCalendar(this.config.modelAvailabilitySchedule, providerId, parsed.model).allowed)
|
|
2105
2130
|
continue;
|
|
2106
|
-
const allowedModels = this.config.providers?.[providerId]?.models;
|
|
2107
|
-
if (allowedModels && !allowedModels.includes(parsed.model)) continue;
|
|
2108
2131
|
resolved.push({
|
|
2109
2132
|
providerId,
|
|
2110
2133
|
model: parsed.model,
|
|
@@ -2122,12 +2145,14 @@ var FallbackProfileManager = class {
|
|
|
2122
2145
|
resolveEffective(opts = {}) {
|
|
2123
2146
|
const bridge = this.resolveBridge(opts.exclude);
|
|
2124
2147
|
let selected = FREEZER_EMPTY;
|
|
2125
|
-
|
|
2126
|
-
|
|
2148
|
+
const explicitRefs = asRefList(opts.fallbackModels);
|
|
2149
|
+
const profileName = asProfileName(opts.fallbackProfile) ?? this.activeProfileName();
|
|
2150
|
+
if (explicitRefs && explicitRefs.length > 0) {
|
|
2151
|
+
const resolved = this.resolveRefs(explicitRefs, opts.exclude);
|
|
2127
2152
|
if (resolved.length > 0) selected = resolved;
|
|
2128
2153
|
}
|
|
2129
|
-
if (selected.length === 0 &&
|
|
2130
|
-
const resolved = this.resolve(
|
|
2154
|
+
if (selected.length === 0 && profileName) {
|
|
2155
|
+
const resolved = this.resolve(profileName, { exclude: opts.exclude });
|
|
2131
2156
|
if (resolved.length > 0) selected = resolved;
|
|
2132
2157
|
}
|
|
2133
2158
|
if (selected.length === 0 && opts.fallbackAuto !== false) {
|
|
@@ -2203,13 +2228,14 @@ var FallbackProfileManager = class {
|
|
|
2203
2228
|
};
|
|
2204
2229
|
const configFallbackAuto = this.config.fallbackAuto;
|
|
2205
2230
|
const effectiveFallbackAuto = configFallbackAuto !== void 0 && configFallbackAuto !== null ? configFallbackAuto : !opts.closedWorld;
|
|
2206
|
-
const explicitRefs = opts.fallbackModels ?? this.config.fallbackModels;
|
|
2231
|
+
const explicitRefs = asRefList(opts.fallbackModels) ?? asRefList(this.config.fallbackModels);
|
|
2232
|
+
const profileName = asProfileName(opts.fallbackProfile) ?? this.activeProfileName();
|
|
2207
2233
|
const explicitUsable = explicitRefs !== void 0 && explicitRefs.length > 0 && this.resolveRefs(explicitRefs, current).length > 0;
|
|
2208
|
-
const profileUsable =
|
|
2234
|
+
const profileUsable = profileName !== void 0 && this.hasProfile(profileName) && this.resolve(profileName, { exclude: current }).length > 0;
|
|
2209
2235
|
const fromExplicitSource = explicitUsable || profileUsable;
|
|
2210
|
-
const selectedChain = opts.closedWorld ? explicitRefs && explicitRefs.length > 0 ? this.resolveRefs(explicitRefs, current) :
|
|
2236
|
+
const selectedChain = opts.closedWorld ? explicitRefs && explicitRefs.length > 0 ? this.resolveRefs(explicitRefs, current) : profileName ? this.resolve(profileName, { exclude: current }) : FREEZER_EMPTY : this.resolveEffective({
|
|
2211
2237
|
fallbackModels: explicitRefs,
|
|
2212
|
-
fallbackProfile:
|
|
2238
|
+
fallbackProfile: profileName,
|
|
2213
2239
|
fallbackAuto: effectiveFallbackAuto,
|
|
2214
2240
|
exclude: current
|
|
2215
2241
|
});
|
|
@@ -2226,7 +2252,7 @@ var FallbackProfileManager = class {
|
|
|
2226
2252
|
});
|
|
2227
2253
|
}
|
|
2228
2254
|
candidates.push(...selectedChain);
|
|
2229
|
-
if (!fromExplicitSource && effectiveFallbackAuto &&
|
|
2255
|
+
if (!fromExplicitSource && effectiveFallbackAuto && profileName !== "default") {
|
|
2230
2256
|
candidates.push(...this.resolve("default", { exclude: current }));
|
|
2231
2257
|
}
|
|
2232
2258
|
if (!fromExplicitSource && effectiveFallbackAuto) {
|
|
@@ -2304,7 +2330,7 @@ var FallbackProfileManager = class {
|
|
|
2304
2330
|
const leaderModel = this.config.model;
|
|
2305
2331
|
const providers = this.config.providers ?? {};
|
|
2306
2332
|
const favoriteSet = new Set(
|
|
2307
|
-
(this.config.favoriteModels ?? []).map((ref) => {
|
|
2333
|
+
(asRefList(this.config.favoriteModels) ?? []).map((ref) => {
|
|
2308
2334
|
const p = parseModelRef(ref);
|
|
2309
2335
|
return `${p.provider ?? leaderProvider}/${p.model}`;
|
|
2310
2336
|
})
|
|
@@ -7478,6 +7504,30 @@ var PROVIDER_MANAGE_SCHEMA = {
|
|
|
7478
7504
|
required: ["action"],
|
|
7479
7505
|
additionalProperties: false
|
|
7480
7506
|
};
|
|
7507
|
+
var CREDENTIAL_SELECTOR_FIELDS = ["apiKey", "apiKeys", "activeKey", "envVars"];
|
|
7508
|
+
function envVarsClaimedByOtherProviders(providers, exceptProvider) {
|
|
7509
|
+
const claimed = /* @__PURE__ */ new Map();
|
|
7510
|
+
for (const [id, entry] of Object.entries(providers)) {
|
|
7511
|
+
if (id === exceptProvider) continue;
|
|
7512
|
+
const names = entry?.["envVars"];
|
|
7513
|
+
if (!Array.isArray(names)) continue;
|
|
7514
|
+
for (const name of names) {
|
|
7515
|
+
if (typeof name === "string" && !claimed.has(name)) claimed.set(name, id);
|
|
7516
|
+
}
|
|
7517
|
+
}
|
|
7518
|
+
return claimed;
|
|
7519
|
+
}
|
|
7520
|
+
function rejectBorrowedEnvVars(providers, provider, requested) {
|
|
7521
|
+
if (!requested) return null;
|
|
7522
|
+
const claimed = envVarsClaimedByOtherProviders(providers, provider);
|
|
7523
|
+
for (const name of requested) {
|
|
7524
|
+
const owner = typeof name === "string" ? claimed.get(name) : void 0;
|
|
7525
|
+
if (owner !== void 0) {
|
|
7526
|
+
return `Environment variable "${String(name)}" already supplies the key for provider "${owner}". Reading another provider's credential from "${provider}" is not allowed; use provider_key_set to give "${provider}" its own key.`;
|
|
7527
|
+
}
|
|
7528
|
+
}
|
|
7529
|
+
return null;
|
|
7530
|
+
}
|
|
7481
7531
|
function validateProviderBaseUrl(raw) {
|
|
7482
7532
|
let url;
|
|
7483
7533
|
try {
|
|
@@ -7548,6 +7598,8 @@ ${msg}`,
|
|
|
7548
7598
|
const invalid = validateProviderBaseUrl(input.baseUrl);
|
|
7549
7599
|
if (invalid) return { status: "error", message: invalid };
|
|
7550
7600
|
}
|
|
7601
|
+
const borrowed = rejectBorrowedEnvVars(providers, input.provider, input.envVars);
|
|
7602
|
+
if (borrowed) return { status: "error", message: borrowed };
|
|
7551
7603
|
const entry = { type: input.type };
|
|
7552
7604
|
if (input.models) entry.models = input.models;
|
|
7553
7605
|
if (input.baseUrl) entry.baseUrl = input.baseUrl;
|
|
@@ -7581,14 +7633,22 @@ ${msg}`,
|
|
|
7581
7633
|
if (input.autoDiscoverModels !== void 0) entry.autoDiscoverModels = input.autoDiscoverModels;
|
|
7582
7634
|
if (input.apiKey !== void 0) entry.apiKey = input.apiKey || void 0;
|
|
7583
7635
|
const endpointChanged = input.baseUrl !== void 0 && (entry.baseUrl ?? void 0) !== (previous.baseUrl ?? void 0);
|
|
7584
|
-
const
|
|
7585
|
-
|
|
7636
|
+
const explicitlySupplied = /* @__PURE__ */ new Set([
|
|
7637
|
+
...input.apiKey !== void 0 ? ["apiKey"] : [],
|
|
7638
|
+
...input.envVars !== void 0 ? ["envVars"] : []
|
|
7639
|
+
]);
|
|
7640
|
+
const droppedFields = endpointChanged ? CREDENTIAL_SELECTOR_FIELDS.filter(
|
|
7641
|
+
(field) => !explicitlySupplied.has(field) && previous[field] !== void 0
|
|
7642
|
+
) : [];
|
|
7643
|
+
for (const field of droppedFields) entry[field] = void 0;
|
|
7644
|
+
const borrowed = rejectBorrowedEnvVars(providers, input.provider, input.envVars);
|
|
7645
|
+
if (borrowed) return { status: "error", message: borrowed };
|
|
7586
7646
|
providers[input.provider] = entry;
|
|
7587
7647
|
await opts.updateConfig((cfg) => {
|
|
7588
7648
|
cfg.providers = providers;
|
|
7589
7649
|
});
|
|
7590
7650
|
const updated = Object.keys({ ...entry }).filter((k) => k !== "apiKey").join(", ");
|
|
7591
|
-
const keyNote =
|
|
7651
|
+
const keyNote = droppedFields.length > 0 ? ` \u2014 cleared ${droppedFields.join(", ")} because the base URL changed; set the key again with provider_key_set` : "";
|
|
7592
7652
|
return { status: "ok", message: `\u2713 Updated ${input.provider}: ${updated}${keyNote}` };
|
|
7593
7653
|
}
|
|
7594
7654
|
if (input.action === "remove") {
|
|
@@ -9494,6 +9554,7 @@ export {
|
|
|
9494
9554
|
createMcpControlTool,
|
|
9495
9555
|
createMcpUseTool,
|
|
9496
9556
|
createOneShotLLMTool,
|
|
9497
|
-
createPluginManagerTool
|
|
9557
|
+
createPluginManagerTool,
|
|
9558
|
+
validateProviderBaseUrl
|
|
9498
9559
|
};
|
|
9499
9560
|
//# sourceMappingURL=index.js.map
|
|
@@ -115,6 +115,20 @@ export interface Config {
|
|
|
115
115
|
* model by `/setmodel`, while the whole ordered list is used for failover.
|
|
116
116
|
*/
|
|
117
117
|
fallbackProfiles?: Record<string, string[]> | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* The named profile from {@link Config.fallbackProfiles} currently selected
|
|
120
|
+
* for failover, set by `/fallback profile use <name>` and cleared by
|
|
121
|
+
* `/fallback profile none`.
|
|
122
|
+
*
|
|
123
|
+
* Resolution order is `fallbackModels` → this profile → smart default, so an
|
|
124
|
+
* explicit chain still wins. Selecting a profile does NOT overwrite
|
|
125
|
+
* `fallbackModels`: before this field existed, `profile use` was implemented
|
|
126
|
+
* by copying the profile's entries over the explicit chain, which destroyed
|
|
127
|
+
* whatever the user had configured and left no way back.
|
|
128
|
+
*
|
|
129
|
+
* A name that no longer matches a defined profile is ignored.
|
|
130
|
+
*/
|
|
131
|
+
fallbackProfile?: string | undefined;
|
|
118
132
|
/**
|
|
119
133
|
* When `true` (the default) and `fallbackModels` is empty, a fallback chain
|
|
120
134
|
* is derived automatically from the other keyed providers/models so 429s
|
package/dist/types/session.d.ts
CHANGED
|
@@ -594,6 +594,13 @@ export interface SessionStore {
|
|
|
594
594
|
* Returns the refreshed summary. Throws if the session does not exist.
|
|
595
595
|
*/
|
|
596
596
|
rename(id: string, name: string): Promise<SessionSummary>;
|
|
597
|
+
/**
|
|
598
|
+
* Return true only when the persisted journal is strictly readable and
|
|
599
|
+
* contains lifecycle envelope events but no messages or other session content.
|
|
600
|
+
* Implementations should fail closed (false) for malformed or unknown events.
|
|
601
|
+
* Optional stores that cannot make this guarantee must omit the method.
|
|
602
|
+
*/
|
|
603
|
+
isEmpty?(id: string): Promise<boolean>;
|
|
597
604
|
delete(id: string): Promise<void>;
|
|
598
605
|
/**
|
|
599
606
|
* Rewrite the session JSONL file to contain only a fresh session_start
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/core",
|
|
3
|
-
"version": "0.306.
|
|
3
|
+
"version": "0.306.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack core: kernel, types, defaults, and shared utilities for the WrongStack CLI agent.",
|
|
6
6
|
"repository": {
|
|
@@ -177,8 +177,8 @@
|
|
|
177
177
|
"wrongstackApiVersion": "0.1.10",
|
|
178
178
|
"dependencies": {
|
|
179
179
|
"zod": "4.4.3",
|
|
180
|
-
"@wrongstack/
|
|
181
|
-
"@wrongstack/
|
|
180
|
+
"@wrongstack/kanban": "0.306.4",
|
|
181
|
+
"@wrongstack/persistence": "0.306.4"
|
|
182
182
|
},
|
|
183
183
|
"devDependencies": {
|
|
184
184
|
"@types/node": "^26.1.2",
|