@wrongstack/core 0.310.1 → 0.313.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chronicle/index.js +84 -6391
- package/dist/chronicle/project-server.js +84 -4868
- package/dist/chunk-2BUUIMQB.js +225 -0
- package/dist/chunk-2FK4WTFF.js +207 -0
- package/dist/chunk-2FQL4PDC.js +292 -0
- package/dist/chunk-2KKODIQU.js +2256 -0
- package/dist/chunk-2YU4WVBW.js +134 -0
- package/dist/chunk-3E244UDA.js +356 -0
- package/dist/chunk-3XPVB7BA.js +135 -0
- package/dist/chunk-4LJJYBAU.js +388 -0
- package/dist/chunk-4VWO3ZXL.js +342 -0
- package/dist/chunk-4XV6IR37.js +187 -0
- package/dist/chunk-4ZHTKJHM.js +183 -0
- package/dist/chunk-52RTVH4M.js +361 -0
- package/dist/chunk-57GEWK5X.js +26 -0
- package/dist/chunk-5DSMEVA4.js +931 -0
- package/dist/chunk-5RP3QTRL.js +1223 -0
- package/dist/chunk-5S437PRC.js +50 -0
- package/dist/chunk-5XDUGTKN.js +511 -0
- package/dist/chunk-62V2AFVG.js +422 -0
- package/dist/chunk-65RDIOR2.js +2470 -0
- package/dist/chunk-6AKOTGLM.js +597 -0
- package/dist/chunk-7ZHZE5EO.js +28 -0
- package/dist/chunk-A455ATZB.js +140 -0
- package/dist/chunk-AD7ZFZCS.js +46 -0
- package/dist/chunk-AI4LLQ2U.js +856 -0
- package/dist/chunk-ASX6AW36.js +789 -0
- package/dist/chunk-AT4V4XGI.js +3522 -0
- package/dist/chunk-AWNQ6ED6.js +311 -0
- package/dist/chunk-B4AMLZPB.js +9 -0
- package/dist/chunk-BAKVJLEV.js +417 -0
- package/dist/chunk-BJHVULHO.js +55 -0
- package/dist/chunk-CB2HFKTP.js +9 -0
- package/dist/chunk-CE4ZOCOI.js +1640 -0
- package/dist/chunk-CQQL4ZOQ.js +151 -0
- package/dist/chunk-DEIQMBD2.js +65 -0
- package/dist/chunk-E6I5RXVK.js +58 -0
- package/dist/chunk-EFEHDQGY.js +1589 -0
- package/dist/chunk-EODDVJBM.js +82 -0
- package/dist/chunk-EPC3GZD4.js +308 -0
- package/dist/chunk-FAYRX2AD.js +13 -0
- package/dist/chunk-FFSX2QDQ.js +362 -0
- package/dist/chunk-FHAMQS7Q.js +5856 -0
- package/dist/chunk-FIV4IJL7.js +2263 -0
- package/dist/chunk-FNXVT7HF.js +52 -0
- package/dist/chunk-FSWCWSTS.js +395 -0
- package/dist/chunk-FW5LDDMJ.js +16464 -0
- package/dist/chunk-FXQLIX3K.js +211 -0
- package/dist/chunk-G5AAKEEZ.js +228 -0
- package/dist/chunk-G5S3S5ZN.js +38 -0
- package/dist/chunk-GAZEWQEZ.js +231 -0
- package/dist/chunk-GFJOMSHG.js +125 -0
- package/dist/chunk-GFXZTHNI.js +158 -0
- package/dist/chunk-GM54GJBU.js +960 -0
- package/dist/chunk-GPQ3NJPR.js +900 -0
- package/dist/chunk-GTCU6DZ3.js +1 -0
- package/dist/chunk-H5E77KUJ.js +15 -0
- package/dist/chunk-H72H4XDU.js +1023 -0
- package/dist/chunk-HA4ZYA6T.js +178 -0
- package/dist/chunk-I45KAW2N.js +5020 -0
- package/dist/chunk-IQ35HMQL.js +1396 -0
- package/dist/chunk-IYFBFX34.js +86 -0
- package/dist/chunk-JJPMWPB6.js +109 -0
- package/dist/chunk-JTHKOYWU.js +663 -0
- package/dist/chunk-JVGPR3TS.js +365 -0
- package/dist/chunk-JVKB2TBL.js +48 -0
- package/dist/chunk-JX7VBZDK.js +31 -0
- package/dist/chunk-K3G6JBUC.js +5232 -0
- package/dist/chunk-K4YUNKU7.js +589 -0
- package/dist/chunk-KBZGE6LN.js +710 -0
- package/dist/chunk-KDQJCT2J.js +45 -0
- package/dist/chunk-KI4L2DUL.js +560 -0
- package/dist/chunk-KJ2ZTQSL.js +47 -0
- package/dist/chunk-KSJ5UTP7.js +154 -0
- package/dist/chunk-LHOA5DWA.js +28 -0
- package/dist/chunk-LX5QWLBL.js +27 -0
- package/dist/chunk-LZDDLNCZ.js +279 -0
- package/dist/chunk-M2WF6AXG.js +115 -0
- package/dist/chunk-M7BS2K6U.js +1 -0
- package/dist/chunk-NDBIPFNY.js +102 -0
- package/dist/chunk-NEMIWEXC.js +1199 -0
- package/dist/chunk-NFAYHPRH.js +323 -0
- package/dist/chunk-O37BQ65S.js +21 -0
- package/dist/chunk-O74MJHXU.js +187 -0
- package/dist/chunk-OJZEX73F.js +112 -0
- package/dist/chunk-PAAWLLZX.js +74 -0
- package/dist/chunk-PFXFQQRT.js +339 -0
- package/dist/chunk-PFZO3C6H.js +14 -0
- package/dist/chunk-PHH7LD45.js +113 -0
- package/dist/chunk-QRTG4XTI.js +188 -0
- package/dist/chunk-QVOVMXJR.js +1351 -0
- package/dist/chunk-RCQYT2I4.js +757 -0
- package/dist/chunk-RD6DEE36.js +1178 -0
- package/dist/chunk-RJIZEJ42.js +30 -0
- package/dist/chunk-RPOA7IAE.js +150 -0
- package/dist/chunk-SN2DQRL6.js +1172 -0
- package/dist/chunk-SQJVFEBV.js +41 -0
- package/dist/chunk-SQP64RCS.js +5391 -0
- package/dist/chunk-SVT4RNTR.js +49 -0
- package/dist/chunk-SWUFN7XM.js +43 -0
- package/dist/chunk-SXMVYC5H.js +174 -0
- package/dist/chunk-SZ6AYUIZ.js +23 -0
- package/dist/chunk-T3KGZBU5.js +3171 -0
- package/dist/chunk-TBRL2RCC.js +9 -0
- package/dist/chunk-TRT2HRWZ.js +18 -0
- package/dist/chunk-TYW2GI5I.js +1077 -0
- package/dist/chunk-UIWONWVG.js +4959 -0
- package/dist/chunk-VB3H5WWB.js +151 -0
- package/dist/chunk-W4A6QA3D.js +258 -0
- package/dist/chunk-WBPVKEEE.js +227 -0
- package/dist/chunk-WEBMSQP2.js +123 -0
- package/dist/chunk-WOEZV3Q4.js +333 -0
- package/dist/chunk-WQQON3SV.js +473 -0
- package/dist/chunk-WQTOVMGV.js +119 -0
- package/dist/chunk-WZO23AWO.js +2779 -0
- package/dist/chunk-XBDP4QPW.js +7 -0
- package/dist/chunk-XEBHSJLT.js +103 -0
- package/dist/chunk-XQ2ECNK3.js +1 -0
- package/dist/chunk-XQ2XGIBF.js +2086 -0
- package/dist/chunk-Y2GE5TSI.js +355 -0
- package/dist/chunk-YBUJCZ57.js +351 -0
- package/dist/chunk-YNCVSFGF.js +149 -0
- package/dist/chunk-YUPMXOGP.js +282 -0
- package/dist/chunk-YY6APYUM.js +49 -0
- package/dist/chunk-Z2IJUO45.js +691 -0
- package/dist/chunk-ZEHUCJHY.js +129 -0
- package/dist/chunk-ZYU2YOJ6.js +1496 -0
- package/dist/chunk-ZZ4V3WIZ.js +163 -0
- package/dist/coordination/agents/index.js +145 -6260
- package/dist/coordination/index.js +365 -34323
- package/dist/coordination/mailbox-project-server.js +48 -3220
- package/dist/coordination/mailbox-types.d.ts +13 -1
- package/dist/coordination/provider-status-tracker.d.ts +4 -0
- package/dist/coordination/sqlite-mailbox.d.ts +2 -1
- package/dist/core/agent.d.ts +14 -1
- package/dist/core/index.js +267 -12479
- package/dist/core/model-ref.js +5 -23
- package/dist/core/provider-runner.d.ts +36 -1
- package/dist/design/index.js +50 -1087
- package/dist/execution/index.d.ts +1 -1
- package/dist/execution/index.js +1599 -21887
- package/dist/execution/prompt-enhancer.js +16 -535
- package/dist/execution/retry-policy.d.ts +31 -1
- package/dist/extension/index.js +7 -311
- package/dist/goal/index.js +33 -3435
- package/dist/hooks/index.js +14 -1138
- package/dist/hq/index.js +112 -5139
- package/dist/hq/protocol.js +21 -668
- package/dist/index.js +1410 -92655
- package/dist/infrastructure/index.js +64 -4811
- package/dist/kernel/events/provider-events.d.ts +15 -0
- package/dist/kernel/events/wrongtrace-events.d.ts +26 -0
- package/dist/kernel/events.d.ts +16 -7
- package/dist/kernel/index.js +14 -992
- package/dist/models/index.js +40 -1117
- package/dist/notifications/index.js +3 -108
- package/dist/observability/index.js +17 -279
- package/dist/plugin/index.js +584 -14835
- package/dist/prompts/index.js +14 -948
- package/dist/registry/index.js +9 -822
- package/dist/registry-R23UBE7H.js +14 -0
- package/dist/replay/index.js +6 -84
- package/dist/review-finding-store-DKAV3AS6.js +21 -0
- package/dist/review-report-store-OWY3EC3N.js +19 -0
- package/dist/security/index.js +114 -2202
- package/dist/session-catalog/index.js +36 -2103
- package/dist/session-catalog/project-server.js +42 -1435
- package/dist/skills/index.js +35 -1578
- package/dist/storage/index.js +182 -15600
- package/dist/tasking/index.js +10 -676
- package/dist/tools/index.js +52 -9589
- package/dist/types/config/autonomy.d.ts +5 -0
- package/dist/types/config/tools.d.ts +2 -2
- package/dist/types/config/ui.d.ts +1 -1
- package/dist/types/errors.d.ts +18 -0
- package/dist/types/index.js +121 -1341
- package/dist/types/provider-runner.d.ts +8 -0
- package/dist/types/session-markers.js +8 -144
- package/dist/utils/error.js +3 -4
- package/dist/utils/expect-defined.js +3 -9
- package/dist/utils/heap-watchdog.js +6 -706
- package/dist/utils/index.js +269 -5685
- package/dist/utils/sage-output-block.js +5 -42
- package/dist/wiring/proxy-rewrite.d.ts +92 -4
- package/dist/wiring/proxy-rewrite.js +20 -61
- package/dist/worktree/index.js +6 -887
- package/package.json +4 -4
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseModelRef
|
|
3
|
+
} from "./chunk-RJIZEJ42.js";
|
|
4
|
+
|
|
5
|
+
// src/core/model-availability-calendar.ts
|
|
6
|
+
function logicalCalendarTarget(providerId, model) {
|
|
7
|
+
if (providerId !== "omniroute") return { providerId, model };
|
|
8
|
+
const slash = model.indexOf("/");
|
|
9
|
+
return slash > 0 && slash < model.length - 1 ? { providerId: model.slice(0, slash), model: model.slice(slash + 1) } : { providerId, model };
|
|
10
|
+
}
|
|
11
|
+
var WEEKDAYS = {
|
|
12
|
+
Sun: 0,
|
|
13
|
+
Mon: 1,
|
|
14
|
+
Tue: 2,
|
|
15
|
+
Wed: 3,
|
|
16
|
+
Thu: 4,
|
|
17
|
+
Fri: 5,
|
|
18
|
+
Sat: 6
|
|
19
|
+
};
|
|
20
|
+
function minuteOfDay(value) {
|
|
21
|
+
const match = /^(\d{2}):(\d{2})$/.exec(value);
|
|
22
|
+
if (!match) return void 0;
|
|
23
|
+
const hour = Number(match[1]);
|
|
24
|
+
const minute = Number(match[2]);
|
|
25
|
+
if (hour > 23 || minute > 59) return void 0;
|
|
26
|
+
return hour * 60 + minute;
|
|
27
|
+
}
|
|
28
|
+
function clockAt(date, timezone) {
|
|
29
|
+
try {
|
|
30
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
31
|
+
timeZone: timezone,
|
|
32
|
+
weekday: "short",
|
|
33
|
+
hour: "2-digit",
|
|
34
|
+
minute: "2-digit",
|
|
35
|
+
hourCycle: "h23"
|
|
36
|
+
}).formatToParts(date);
|
|
37
|
+
const weekday = parts.find((part) => part.type === "weekday")?.value;
|
|
38
|
+
const hour = Number(parts.find((part) => part.type === "hour")?.value);
|
|
39
|
+
const minute = Number(parts.find((part) => part.type === "minute")?.value);
|
|
40
|
+
if (!weekday || WEEKDAYS[weekday] === void 0 || !Number.isFinite(hour + minute))
|
|
41
|
+
return void 0;
|
|
42
|
+
return { day: WEEKDAYS[weekday], minute: hour * 60 + minute };
|
|
43
|
+
} catch {
|
|
44
|
+
return void 0;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function targetMatches(rule, providerId, model) {
|
|
48
|
+
if (rule.provider && rule.provider !== providerId) return false;
|
|
49
|
+
if (rule.model && rule.model !== model) return false;
|
|
50
|
+
return Boolean(rule.provider || rule.model);
|
|
51
|
+
}
|
|
52
|
+
function timeMatches(rule, day, minute) {
|
|
53
|
+
const start = minuteOfDay(rule.start);
|
|
54
|
+
const end = minuteOfDay(rule.end);
|
|
55
|
+
if (start === void 0 || end === void 0) return false;
|
|
56
|
+
const days = rule.days?.length ? new Set(rule.days) : void 0;
|
|
57
|
+
if (start === end) return !days || days.has(day);
|
|
58
|
+
if (start < end) return (!days || days.has(day)) && minute >= start && minute < end;
|
|
59
|
+
if (minute >= start) return !days || days.has(day);
|
|
60
|
+
const previousDay = (day + 6) % 7;
|
|
61
|
+
return minute < end && (!days || days.has(previousDay));
|
|
62
|
+
}
|
|
63
|
+
function evaluateModelCalendar(rules, providerId, model, at = /* @__PURE__ */ new Date()) {
|
|
64
|
+
({ providerId, model } = logicalCalendarTarget(providerId, model));
|
|
65
|
+
const allowRules = [];
|
|
66
|
+
let allowMatched = false;
|
|
67
|
+
for (const rule of rules ?? []) {
|
|
68
|
+
if (rule.enabled === false || !targetMatches(rule, providerId, model)) continue;
|
|
69
|
+
const clock = clockAt(at, rule.timezone);
|
|
70
|
+
if (!clock || minuteOfDay(rule.start) === void 0 || minuteOfDay(rule.end) === void 0)
|
|
71
|
+
continue;
|
|
72
|
+
if (rule.mode === "allow_only") {
|
|
73
|
+
allowRules.push(rule);
|
|
74
|
+
if (timeMatches(rule, clock.day, clock.minute)) allowMatched = true;
|
|
75
|
+
} else if (timeMatches(rule, clock.day, clock.minute)) {
|
|
76
|
+
return { allowed: false, rule };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (allowRules.length > 0 && !allowMatched) return { allowed: false, rule: allowRules[0] };
|
|
80
|
+
return { allowed: true };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/core/fallback-profile-manager.ts
|
|
84
|
+
function hasText(value) {
|
|
85
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
86
|
+
}
|
|
87
|
+
function asRefList(value) {
|
|
88
|
+
return Array.isArray(value) ? value : void 0;
|
|
89
|
+
}
|
|
90
|
+
function asProfileName(value) {
|
|
91
|
+
return hasText(value) ? value : void 0;
|
|
92
|
+
}
|
|
93
|
+
function providerHasKey(entry) {
|
|
94
|
+
if (!entry) return false;
|
|
95
|
+
if (hasText(entry.apiKey)) return true;
|
|
96
|
+
if (Array.isArray(entry.apiKeys) && entry.apiKeys.some((k) => hasText(k?.apiKey))) return true;
|
|
97
|
+
if (Array.isArray(entry.envVars) && entry.envVars.some((v) => hasText(process.env[v])))
|
|
98
|
+
return true;
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
function visibleProviderModels(config, providerId, providerModels) {
|
|
102
|
+
const entry = config.providers?.[providerId];
|
|
103
|
+
return Array.isArray(entry?.models) ? [...entry.models] : providerModels;
|
|
104
|
+
}
|
|
105
|
+
function buildProfiles(config) {
|
|
106
|
+
const entries = /* @__PURE__ */ new Map();
|
|
107
|
+
for (const [name, chain] of Object.entries(config.fallbackProfiles ?? {})) {
|
|
108
|
+
if (Array.isArray(chain) && chain.length > 0) {
|
|
109
|
+
entries.set(name, Object.freeze([...chain]));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return entries;
|
|
113
|
+
}
|
|
114
|
+
var FallbackProfileManager = class {
|
|
115
|
+
/** Immutable snapshot of config.fallbackProfiles for the active config. */
|
|
116
|
+
profiles;
|
|
117
|
+
/** Active frozen config snapshot for provider lookups. */
|
|
118
|
+
config;
|
|
119
|
+
/** Optional shared runtime status tracker. */
|
|
120
|
+
statusTracker;
|
|
121
|
+
constructor(config, opts) {
|
|
122
|
+
this.config = config;
|
|
123
|
+
this.profiles = buildProfiles(config);
|
|
124
|
+
this.statusTracker = opts?.statusTracker;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Bind (or replace) the shared runtime status tracker.
|
|
128
|
+
* Called by the boot path after the tracker is created.
|
|
129
|
+
*/
|
|
130
|
+
setStatusTracker(tracker) {
|
|
131
|
+
this.statusTracker = tracker;
|
|
132
|
+
}
|
|
133
|
+
// ── Profile existence ──────────────────────────────────────────────────
|
|
134
|
+
hasProfile(name) {
|
|
135
|
+
return this.profiles.has(name);
|
|
136
|
+
}
|
|
137
|
+
listProfiles() {
|
|
138
|
+
return Object.freeze([...this.profiles.keys()]);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The profile the session has selected (`config.fallbackProfile`, set by
|
|
142
|
+
* `/fallback profile use <name>`), or undefined when none is selected or the
|
|
143
|
+
* name no longer resolves to a defined profile.
|
|
144
|
+
*
|
|
145
|
+
* Consulted by every resolution entry point when the caller does not name a
|
|
146
|
+
* profile itself. Without this the leader — which passes no profile — could
|
|
147
|
+
* never use a named profile at all: `config.fallbackProfiles` was reachable
|
|
148
|
+
* only by copying a chain into `fallbackModels`.
|
|
149
|
+
*/
|
|
150
|
+
activeProfileName() {
|
|
151
|
+
const name = asProfileName(this.config.fallbackProfile);
|
|
152
|
+
return name && this.profiles.has(name) ? name : void 0;
|
|
153
|
+
}
|
|
154
|
+
// ── Resolution ─────────────────────────────────────────────────────────
|
|
155
|
+
/**
|
|
156
|
+
* Resolve a named fallback profile to a validated, provider-filtered chain.
|
|
157
|
+
*
|
|
158
|
+
* Returns an empty chain when the profile doesn't exist, or when every entry
|
|
159
|
+
* is excluded, quarantined, or blacked out.
|
|
160
|
+
*
|
|
161
|
+
* Filtering is intentionally identical to {@link resolveRefs} (the explicit
|
|
162
|
+
* `fallbackModels` path): the self-exclusion, the runtime status tracker,
|
|
163
|
+
* and the availability calendar — nothing else. Anything a named profile
|
|
164
|
+
* drops, an explicit chain drops too, and vice versa. Profiles used to apply
|
|
165
|
+
* two extra filters (provider "usability" and the `providers[].models`
|
|
166
|
+
* snapshot) that the explicit path did not, which silently rerouted roles
|
|
167
|
+
* pinned to a profile onto a different model than the one configured.
|
|
168
|
+
*
|
|
169
|
+
* @param name - Profile name from config.fallbackProfiles.
|
|
170
|
+
* @param defaultProvider - Used when an entry has no explicit provider.
|
|
171
|
+
* @param exclude - Optional { providerId, model } to skip (avoid self-fallback).
|
|
172
|
+
*/
|
|
173
|
+
resolve(name, opts = {}) {
|
|
174
|
+
const defaultProvider = opts.defaultProvider ?? this.config.provider;
|
|
175
|
+
const chain = this.profiles.get(name);
|
|
176
|
+
if (!chain) return FREEZER_EMPTY;
|
|
177
|
+
const excludeKey = opts.exclude ? `${opts.exclude.providerId}/${opts.exclude.model}` : void 0;
|
|
178
|
+
const resolved = [];
|
|
179
|
+
const seen = /* @__PURE__ */ new Set();
|
|
180
|
+
for (const ref of chain) {
|
|
181
|
+
const parsed = parseModelRef(ref);
|
|
182
|
+
if (!parsed.model) continue;
|
|
183
|
+
const providerId = parsed.provider ?? defaultProvider;
|
|
184
|
+
const key = `${providerId}/${parsed.model}`;
|
|
185
|
+
if (seen.has(key)) continue;
|
|
186
|
+
seen.add(key);
|
|
187
|
+
if (excludeKey && key === excludeKey) continue;
|
|
188
|
+
if (this.statusTracker && !this.statusTracker.isAvailable(providerId, parsed.model)) continue;
|
|
189
|
+
if (!evaluateModelCalendar(this.config.modelAvailabilitySchedule, providerId, parsed.model).allowed)
|
|
190
|
+
continue;
|
|
191
|
+
resolved.push({
|
|
192
|
+
providerId,
|
|
193
|
+
model: parsed.model,
|
|
194
|
+
providerSwitched: providerId !== (opts.exclude?.providerId ?? this.config.provider)
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
return Object.freeze(resolved);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Resolve the effective fallback chain for a session: explicit fallbackModels
|
|
201
|
+
* first, then named profile, then smart default (unless disabled).
|
|
202
|
+
*
|
|
203
|
+
* Mirrors the previous `effectiveFallbackChain()` logic but centralized.
|
|
204
|
+
*/
|
|
205
|
+
resolveEffective(opts = {}) {
|
|
206
|
+
const bridge = this.resolveBridge(opts.exclude);
|
|
207
|
+
let selected = FREEZER_EMPTY;
|
|
208
|
+
const explicitRefs = asRefList(opts.fallbackModels);
|
|
209
|
+
const profileName = asProfileName(opts.fallbackProfile) ?? this.activeProfileName();
|
|
210
|
+
if (explicitRefs && explicitRefs.length > 0) {
|
|
211
|
+
const resolved = this.resolveRefs(explicitRefs, opts.exclude);
|
|
212
|
+
if (resolved.length > 0) selected = resolved;
|
|
213
|
+
}
|
|
214
|
+
if (selected.length === 0 && profileName) {
|
|
215
|
+
const resolved = this.resolve(profileName, { exclude: opts.exclude });
|
|
216
|
+
if (resolved.length > 0) selected = resolved;
|
|
217
|
+
}
|
|
218
|
+
if (selected.length === 0 && opts.fallbackAuto !== false) {
|
|
219
|
+
selected = this.smartDefault(opts.exclude);
|
|
220
|
+
}
|
|
221
|
+
if (bridge.length === 0) return selected;
|
|
222
|
+
if (selected.length === 0) return bridge;
|
|
223
|
+
const seen = new Set(bridge.map((entry) => `${entry.providerId}/${entry.model}`));
|
|
224
|
+
return Object.freeze([
|
|
225
|
+
...bridge,
|
|
226
|
+
...selected.filter((entry) => !seen.has(`${entry.providerId}/${entry.model}`))
|
|
227
|
+
]);
|
|
228
|
+
}
|
|
229
|
+
/** Resolve the optional, fully-qualified emergency continuity route. */
|
|
230
|
+
resolveBridge(exclude) {
|
|
231
|
+
const ref = this.config.fallbackBridge?.trim();
|
|
232
|
+
if (!ref) return FREEZER_EMPTY;
|
|
233
|
+
const parsed = parseModelRef(ref);
|
|
234
|
+
if (!parsed.provider || !parsed.model) return FREEZER_EMPTY;
|
|
235
|
+
return this.resolveRefs([ref], exclude);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Resolve every usable configured target as a bounded last-resort chain.
|
|
239
|
+
* Normal smart defaults stay bounded; callers append this only after the
|
|
240
|
+
* preferred chain and only when automatic fallback is enabled. The cap
|
|
241
|
+
* ({@link MAX_LAST_RESORT_CANDIDATES}) prevents a config with many providers
|
|
242
|
+
* from producing a degenerate chain of doomed requests during a systemic
|
|
243
|
+
* outage — by this point the smart default, bridge, and default profile
|
|
244
|
+
* have already failed.
|
|
245
|
+
*/
|
|
246
|
+
resolveAllConfigured(exclude) {
|
|
247
|
+
return this.smartDefault(exclude, this.lastResortCap());
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Effective cap for the last-resort append. Reads the user-configurable
|
|
251
|
+
* {@link Config.fallbackMaxLastResortCandidates} when set and valid;
|
|
252
|
+
* otherwise falls back to the compiled-in default
|
|
253
|
+
* {@link MAX_LAST_RESORT_CANDIDATES}.
|
|
254
|
+
*/
|
|
255
|
+
lastResortCap() {
|
|
256
|
+
const configured = this.config.fallbackMaxLastResortCandidates;
|
|
257
|
+
if (typeof configured === "number" && Number.isFinite(configured) && configured >= 0) {
|
|
258
|
+
return Math.floor(configured);
|
|
259
|
+
}
|
|
260
|
+
return MAX_LAST_RESORT_CANDIDATES;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Build the complete fallback candidate chain shared by the agent-loop
|
|
264
|
+
* extension and the one-shot orchestrator. Centralizes the bridge → primary →
|
|
265
|
+
* selected → default-profile → all-configured ladder and the fromExplicitSource
|
|
266
|
+
* gate so both consumers produce identical ordering and depth semantics.
|
|
267
|
+
*
|
|
268
|
+
* Layering (each step deduped against all prior):
|
|
269
|
+
* 1. Bridge (emergency continuity route).
|
|
270
|
+
* 2. Configured primary, when the live context drifted from it.
|
|
271
|
+
* 3. The selected chain (explicit refs → named profile → smart default),
|
|
272
|
+
* via {@link resolveEffective}.
|
|
273
|
+
* 4. The "default" profile — extra depth, ONLY when the chain was auto-derived.
|
|
274
|
+
* 5. Every other configured provider — last resort, ONLY when the chain was
|
|
275
|
+
* auto-derived AND `effectiveFallbackAuto` is true.
|
|
276
|
+
*
|
|
277
|
+
* Returns the empty chain when `closedWorld` is true and no explicit
|
|
278
|
+
* refs/profile resolved — a model allowlist never leaks to unlisted models.
|
|
279
|
+
*
|
|
280
|
+
* @internal caller-aware options (primary, closedWorld) are accepted because
|
|
281
|
+
* the agent loop has context the manager does not own; the resolution
|
|
282
|
+
* pipeline itself is identical for both callers.
|
|
283
|
+
*/
|
|
284
|
+
resolveCandidates(current, opts = {}) {
|
|
285
|
+
const configuredPrimary = opts.primary ?? {
|
|
286
|
+
providerId: this.config.provider,
|
|
287
|
+
model: this.config.model
|
|
288
|
+
};
|
|
289
|
+
const configFallbackAuto = this.config.fallbackAuto;
|
|
290
|
+
const effectiveFallbackAuto = configFallbackAuto !== void 0 && configFallbackAuto !== null ? configFallbackAuto : !opts.closedWorld;
|
|
291
|
+
const explicitRefs = asRefList(opts.fallbackModels) ?? asRefList(this.config.fallbackModels);
|
|
292
|
+
const profileName = asProfileName(opts.fallbackProfile) ?? this.activeProfileName();
|
|
293
|
+
const explicitUsable = explicitRefs !== void 0 && explicitRefs.length > 0 && this.resolveRefs(explicitRefs, current).length > 0;
|
|
294
|
+
const profileUsable = profileName !== void 0 && this.hasProfile(profileName) && this.resolve(profileName, { exclude: current }).length > 0;
|
|
295
|
+
const fromExplicitSource = explicitUsable || profileUsable;
|
|
296
|
+
const selectedChain = opts.closedWorld ? explicitRefs && explicitRefs.length > 0 ? this.resolveRefs(explicitRefs, current) : profileName ? this.resolve(profileName, { exclude: current }) : FREEZER_EMPTY : this.resolveEffective({
|
|
297
|
+
fallbackModels: explicitRefs,
|
|
298
|
+
fallbackProfile: profileName,
|
|
299
|
+
fallbackAuto: effectiveFallbackAuto,
|
|
300
|
+
exclude: current
|
|
301
|
+
});
|
|
302
|
+
const candidates = [];
|
|
303
|
+
if (opts.closedWorld) {
|
|
304
|
+
candidates.push(...selectedChain);
|
|
305
|
+
} else {
|
|
306
|
+
candidates.push(...this.resolveBridge(current));
|
|
307
|
+
if (!(configuredPrimary.providerId === current.providerId && configuredPrimary.model === current.model)) {
|
|
308
|
+
candidates.push({
|
|
309
|
+
providerId: configuredPrimary.providerId,
|
|
310
|
+
model: configuredPrimary.model,
|
|
311
|
+
providerSwitched: configuredPrimary.providerId !== current.providerId
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
candidates.push(...selectedChain);
|
|
315
|
+
if (!fromExplicitSource && effectiveFallbackAuto && profileName !== "default") {
|
|
316
|
+
candidates.push(...this.resolve("default", { exclude: current }));
|
|
317
|
+
}
|
|
318
|
+
if (!fromExplicitSource && effectiveFallbackAuto) {
|
|
319
|
+
const cap = this.lastResortCap();
|
|
320
|
+
if (cap > 0) {
|
|
321
|
+
const usedKeys = new Set(
|
|
322
|
+
candidates.map((c) => `${c.providerId}/${c.model}`)
|
|
323
|
+
);
|
|
324
|
+
const lastResort = this.smartDefault(current, Number.POSITIVE_INFINITY).filter((c) => !usedKeys.has(`${c.providerId}/${c.model}`)).slice(0, cap);
|
|
325
|
+
candidates.push(...lastResort);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return dedupeChain(candidates, current);
|
|
330
|
+
}
|
|
331
|
+
// ── Provider availability (read-only) ──────────────────────────────────
|
|
332
|
+
checkProvider(providerId) {
|
|
333
|
+
const entry = this.config.providers?.[providerId];
|
|
334
|
+
const isPrimary = providerId === this.config.provider;
|
|
335
|
+
const hasKey = providerHasKey(entry) || isPrimary && hasText(this.config.apiKey);
|
|
336
|
+
const hasEndpoint = hasText(entry?.baseUrl) || isPrimary && hasText(this.config.baseUrl);
|
|
337
|
+
const hasModels = Array.isArray(entry?.models) && entry.models.length > 0 || isPrimary && hasText(this.config.model);
|
|
338
|
+
return Object.freeze({
|
|
339
|
+
providerId,
|
|
340
|
+
hasKey,
|
|
341
|
+
hasEndpoint,
|
|
342
|
+
hasModels,
|
|
343
|
+
usable: hasKey || hasEndpoint
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
// ── Rebuild on config change ───────────────────────────────────────────
|
|
347
|
+
/**
|
|
348
|
+
* Atomically replace the active immutable snapshot while preserving this
|
|
349
|
+
* service identity for every injected consumer.
|
|
350
|
+
*/
|
|
351
|
+
reload(newConfig) {
|
|
352
|
+
this.config = newConfig;
|
|
353
|
+
this.profiles = buildProfiles(newConfig);
|
|
354
|
+
}
|
|
355
|
+
// ── Internal helpers ───────────────────────────────────────────────────
|
|
356
|
+
/**
|
|
357
|
+
* Resolve an array of model ref strings (from explicit fallbackModels config).
|
|
358
|
+
* Public so consumers like one-shot-llm can use it directly.
|
|
359
|
+
*/
|
|
360
|
+
resolveRefs(refs, exclude) {
|
|
361
|
+
const excludeKey = exclude ? `${exclude.providerId}/${exclude.model}` : void 0;
|
|
362
|
+
const resolved = [];
|
|
363
|
+
const seen = /* @__PURE__ */ new Set();
|
|
364
|
+
for (const ref of refs) {
|
|
365
|
+
const parsed = parseModelRef(ref);
|
|
366
|
+
if (!parsed.model) continue;
|
|
367
|
+
const providerId = parsed.provider ?? this.config.provider;
|
|
368
|
+
const key = `${providerId}/${parsed.model}`;
|
|
369
|
+
if (seen.has(key)) continue;
|
|
370
|
+
seen.add(key);
|
|
371
|
+
if (excludeKey && key === excludeKey) continue;
|
|
372
|
+
if (this.statusTracker && !this.statusTracker.isAvailable(providerId, parsed.model)) continue;
|
|
373
|
+
if (!evaluateModelCalendar(this.config.modelAvailabilitySchedule, providerId, parsed.model).allowed)
|
|
374
|
+
continue;
|
|
375
|
+
resolved.push({
|
|
376
|
+
providerId,
|
|
377
|
+
model: parsed.model,
|
|
378
|
+
providerSwitched: providerId !== (exclude?.providerId ?? this.config.provider)
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
return Object.freeze(resolved);
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Derive a smart default chain from configured providers when nothing
|
|
385
|
+
* explicit is set. Same-provider alternatives first, then cross-provider.
|
|
386
|
+
* Limited to 4 entries to avoid burning through models on a transient blip.
|
|
387
|
+
*/
|
|
388
|
+
smartDefault(exclude, maxCandidates = 4) {
|
|
389
|
+
const leaderProvider = this.config.provider;
|
|
390
|
+
const leaderModel = this.config.model;
|
|
391
|
+
const providers = this.config.providers ?? {};
|
|
392
|
+
const favoriteSet = new Set(
|
|
393
|
+
(asRefList(this.config.favoriteModels) ?? []).map((ref) => {
|
|
394
|
+
const p = parseModelRef(ref);
|
|
395
|
+
return `${p.provider ?? leaderProvider}/${p.model}`;
|
|
396
|
+
})
|
|
397
|
+
);
|
|
398
|
+
const hasFavorites = favoriteSet.size > 0;
|
|
399
|
+
const favoritesOnly = this.config.favoriteModelsOnly === true;
|
|
400
|
+
const seen = /* @__PURE__ */ new Set();
|
|
401
|
+
const favorites = [];
|
|
402
|
+
const sameProvider = [];
|
|
403
|
+
const crossProvider = [];
|
|
404
|
+
const excludeKey = exclude ? `${exclude.providerId}/${exclude.model}` : void 0;
|
|
405
|
+
const ids = Object.keys(providers).sort(
|
|
406
|
+
(a, b) => a === leaderProvider ? -1 : b === leaderProvider ? 1 : a.localeCompare(b)
|
|
407
|
+
);
|
|
408
|
+
for (const id of ids) {
|
|
409
|
+
const entry = providers[id];
|
|
410
|
+
if (!this.checkProvider(id).usable) continue;
|
|
411
|
+
const models = visibleProviderModels(this.config, id, entry?.models ?? []);
|
|
412
|
+
for (const model of models) {
|
|
413
|
+
if (id === leaderProvider && model === leaderModel) continue;
|
|
414
|
+
const ref = `${id}/${model}`;
|
|
415
|
+
if (seen.has(ref)) continue;
|
|
416
|
+
seen.add(ref);
|
|
417
|
+
if (excludeKey && ref === excludeKey) continue;
|
|
418
|
+
if (this.statusTracker && !this.statusTracker.isAvailable(id, model)) continue;
|
|
419
|
+
if (!evaluateModelCalendar(this.config.modelAvailabilitySchedule, id, model).allowed)
|
|
420
|
+
continue;
|
|
421
|
+
if (favoriteSet.has(ref)) {
|
|
422
|
+
favorites.push(ref);
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (favoritesOnly && hasFavorites) continue;
|
|
426
|
+
(id === leaderProvider ? sameProvider : crossProvider).push(ref);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
const ordered = [...favorites, ...sameProvider, ...crossProvider];
|
|
430
|
+
const all = Number.isFinite(maxCandidates) ? ordered.slice(0, Math.max(0, maxCandidates)) : [...ordered];
|
|
431
|
+
const firstCrossProvider = ordered.find((ref) => {
|
|
432
|
+
const parsed = parseModelRef(ref);
|
|
433
|
+
return (parsed.provider ?? leaderProvider) !== leaderProvider;
|
|
434
|
+
});
|
|
435
|
+
if (all.length > 0 && firstCrossProvider && !all.some((ref) => {
|
|
436
|
+
const parsed = parseModelRef(ref);
|
|
437
|
+
return (parsed.provider ?? leaderProvider) !== leaderProvider;
|
|
438
|
+
})) {
|
|
439
|
+
all[all.length - 1] = firstCrossProvider;
|
|
440
|
+
}
|
|
441
|
+
return Object.freeze(
|
|
442
|
+
all.map((ref) => {
|
|
443
|
+
const p = parseModelRef(ref);
|
|
444
|
+
return {
|
|
445
|
+
providerId: p.provider ?? leaderProvider,
|
|
446
|
+
model: p.model,
|
|
447
|
+
providerSwitched: (p.provider ?? leaderProvider) !== (exclude?.providerId ?? leaderProvider)
|
|
448
|
+
};
|
|
449
|
+
})
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
var MAX_LAST_RESORT_CANDIDATES = 12;
|
|
454
|
+
var FREEZER_EMPTY = Object.freeze([]);
|
|
455
|
+
function dedupeChain(entries, current) {
|
|
456
|
+
const seen = /* @__PURE__ */ new Set();
|
|
457
|
+
const currentKey = `${current.providerId}/${current.model}`;
|
|
458
|
+
return Object.freeze(
|
|
459
|
+
entries.filter((entry) => {
|
|
460
|
+
const key = `${entry.providerId}/${entry.model}`;
|
|
461
|
+
if (key === currentKey || seen.has(key)) return false;
|
|
462
|
+
seen.add(key);
|
|
463
|
+
return true;
|
|
464
|
+
})
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export {
|
|
469
|
+
logicalCalendarTarget,
|
|
470
|
+
evaluateModelCalendar,
|
|
471
|
+
FallbackProfileManager
|
|
472
|
+
};
|
|
473
|
+
//# sourceMappingURL=chunk-WQQON3SV.js.map
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// src/utils/tool-description-mode.ts
|
|
2
|
+
var DEFAULT_TOOL_DESCRIPTION_MODE = "extend";
|
|
3
|
+
var ORIGINAL_TOOL_DESCRIPTION = /* @__PURE__ */ Symbol.for("wrongstack.tool.originalDescription");
|
|
4
|
+
function normalizeToolDescriptionMode(value) {
|
|
5
|
+
if (typeof value !== "string") return void 0;
|
|
6
|
+
const raw = value.trim().toLowerCase();
|
|
7
|
+
if (raw === "extend" || raw === "extended" || raw === "full") return "extend";
|
|
8
|
+
if (raw === "simple" || raw === "short" || raw === "brief") return "simple";
|
|
9
|
+
return void 0;
|
|
10
|
+
}
|
|
11
|
+
function resolveToolDescriptionMode(modes, toolName) {
|
|
12
|
+
return normalizeToolDescriptionMode(modes?.[toolName]) ?? DEFAULT_TOOL_DESCRIPTION_MODE;
|
|
13
|
+
}
|
|
14
|
+
function simplifyToolDescription(text, opts = {}) {
|
|
15
|
+
const maxSentences = Math.max(1, opts.maxSentences ?? 2);
|
|
16
|
+
const maxChars = Math.max(40, opts.maxChars ?? 180);
|
|
17
|
+
const normalized = text.replace(/\r\n?/g, "\n").split("\n").map((line) => line.trim()).filter(Boolean).join(" ").replace(/\s+/g, " ").trim();
|
|
18
|
+
if (normalized.length <= maxChars) return normalized;
|
|
19
|
+
const sentences = normalized.match(/[^.!?]+[.!?]+(?=\s|$)|[^.!?]+$/g) ?? [normalized];
|
|
20
|
+
const selected = [];
|
|
21
|
+
for (const sentence of sentences) {
|
|
22
|
+
selected.push(sentence.trim());
|
|
23
|
+
const candidate = selected.join(" ");
|
|
24
|
+
if (selected.length >= maxSentences || candidate.length >= maxChars) break;
|
|
25
|
+
}
|
|
26
|
+
const summary = selected.join(" ").trim() || normalized;
|
|
27
|
+
if (summary.length <= maxChars) return summary;
|
|
28
|
+
const hardLimit = maxChars - 4;
|
|
29
|
+
const boundary = findWordBoundary(summary, hardLimit);
|
|
30
|
+
return `${summary.slice(0, boundary > 0 ? boundary : hardLimit).trimEnd()} ...`;
|
|
31
|
+
}
|
|
32
|
+
function applyToolDescriptionModeToTool(tool, mode) {
|
|
33
|
+
const existingOriginal = getOriginalDescription(tool);
|
|
34
|
+
if (mode === "extend" && !existingOriginal) return tool;
|
|
35
|
+
const original = existingOriginal ?? {
|
|
36
|
+
description: tool.description,
|
|
37
|
+
usageHint: tool.usageHint
|
|
38
|
+
};
|
|
39
|
+
const next = mode === "simple" ? withDescription(tool, {
|
|
40
|
+
description: simplifyToolDescription(original.description),
|
|
41
|
+
usageHint: original.usageHint === void 0 ? void 0 : simplifyToolDescription(original.usageHint)
|
|
42
|
+
}) : withDescription(tool, original);
|
|
43
|
+
return attachOriginalDescription(next, original);
|
|
44
|
+
}
|
|
45
|
+
function setToolDescriptionMode(registry, name, mode) {
|
|
46
|
+
if (typeof registry.setDescriptionMode === "function") {
|
|
47
|
+
return registry.setDescriptionMode(name, mode);
|
|
48
|
+
}
|
|
49
|
+
if (!registry.get(name) || typeof registry.wrap !== "function") return false;
|
|
50
|
+
registry.wrap(
|
|
51
|
+
name,
|
|
52
|
+
(tool) => applyToolDescriptionModeToTool(tool, mode),
|
|
53
|
+
"tool-description-mode"
|
|
54
|
+
);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
function getToolDescriptionMode(registry, name) {
|
|
58
|
+
return registry.getDescriptionMode?.(name) ?? DEFAULT_TOOL_DESCRIPTION_MODE;
|
|
59
|
+
}
|
|
60
|
+
function applyToolDescriptionModes(registry, modes) {
|
|
61
|
+
if (typeof registry.applyDescriptionModes === "function") {
|
|
62
|
+
return registry.applyDescriptionModes(modes);
|
|
63
|
+
}
|
|
64
|
+
const entries = Object.entries(modes ?? {});
|
|
65
|
+
const missing = [];
|
|
66
|
+
let applied = 0;
|
|
67
|
+
for (const [name, rawMode] of entries) {
|
|
68
|
+
const mode = normalizeToolDescriptionMode(rawMode);
|
|
69
|
+
if (!mode) continue;
|
|
70
|
+
if (setToolDescriptionMode(registry, name, mode)) applied++;
|
|
71
|
+
else missing.push(name);
|
|
72
|
+
}
|
|
73
|
+
return { applied, missing };
|
|
74
|
+
}
|
|
75
|
+
function getOriginalDescription(tool) {
|
|
76
|
+
return tool[ORIGINAL_TOOL_DESCRIPTION];
|
|
77
|
+
}
|
|
78
|
+
function attachOriginalDescription(tool, original) {
|
|
79
|
+
Object.defineProperty(tool, ORIGINAL_TOOL_DESCRIPTION, {
|
|
80
|
+
configurable: true,
|
|
81
|
+
enumerable: false,
|
|
82
|
+
value: original,
|
|
83
|
+
writable: true
|
|
84
|
+
});
|
|
85
|
+
return tool;
|
|
86
|
+
}
|
|
87
|
+
function withDescription(tool, next) {
|
|
88
|
+
const copy = {
|
|
89
|
+
...tool,
|
|
90
|
+
description: next.description,
|
|
91
|
+
usageHint: next.usageHint
|
|
92
|
+
};
|
|
93
|
+
if (next.usageHint === void 0) {
|
|
94
|
+
delete copy.usageHint;
|
|
95
|
+
}
|
|
96
|
+
return copy;
|
|
97
|
+
}
|
|
98
|
+
function findWordBoundary(text, limit) {
|
|
99
|
+
const semantic = Math.max(
|
|
100
|
+
text.lastIndexOf(". ", limit),
|
|
101
|
+
text.lastIndexOf("; ", limit),
|
|
102
|
+
text.lastIndexOf(", ", limit)
|
|
103
|
+
);
|
|
104
|
+
if (semantic > 40) return semantic + 1;
|
|
105
|
+
const space = text.lastIndexOf(" ", limit);
|
|
106
|
+
return space > 40 ? space : limit;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export {
|
|
110
|
+
DEFAULT_TOOL_DESCRIPTION_MODE,
|
|
111
|
+
normalizeToolDescriptionMode,
|
|
112
|
+
resolveToolDescriptionMode,
|
|
113
|
+
simplifyToolDescription,
|
|
114
|
+
applyToolDescriptionModeToTool,
|
|
115
|
+
setToolDescriptionMode,
|
|
116
|
+
getToolDescriptionMode,
|
|
117
|
+
applyToolDescriptionModes
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=chunk-WQTOVMGV.js.map
|