@wrongstack/core 0.313.0 → 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.
Files changed (182) hide show
  1. package/dist/chronicle/index.js +84 -6391
  2. package/dist/chronicle/project-server.js +84 -4868
  3. package/dist/chunk-2BUUIMQB.js +225 -0
  4. package/dist/chunk-2FK4WTFF.js +207 -0
  5. package/dist/chunk-2FQL4PDC.js +292 -0
  6. package/dist/chunk-2KKODIQU.js +2256 -0
  7. package/dist/chunk-2YU4WVBW.js +134 -0
  8. package/dist/chunk-3E244UDA.js +356 -0
  9. package/dist/chunk-3XPVB7BA.js +135 -0
  10. package/dist/chunk-4LJJYBAU.js +388 -0
  11. package/dist/chunk-4VWO3ZXL.js +342 -0
  12. package/dist/chunk-4XV6IR37.js +187 -0
  13. package/dist/chunk-4ZHTKJHM.js +183 -0
  14. package/dist/chunk-52RTVH4M.js +361 -0
  15. package/dist/chunk-57GEWK5X.js +26 -0
  16. package/dist/chunk-5DSMEVA4.js +931 -0
  17. package/dist/chunk-5RP3QTRL.js +1223 -0
  18. package/dist/chunk-5S437PRC.js +50 -0
  19. package/dist/chunk-5XDUGTKN.js +511 -0
  20. package/dist/chunk-62V2AFVG.js +422 -0
  21. package/dist/chunk-65RDIOR2.js +2470 -0
  22. package/dist/chunk-6AKOTGLM.js +597 -0
  23. package/dist/chunk-7ZHZE5EO.js +28 -0
  24. package/dist/chunk-A455ATZB.js +140 -0
  25. package/dist/chunk-AD7ZFZCS.js +46 -0
  26. package/dist/chunk-AI4LLQ2U.js +856 -0
  27. package/dist/chunk-ASX6AW36.js +789 -0
  28. package/dist/chunk-AT4V4XGI.js +3522 -0
  29. package/dist/chunk-AWNQ6ED6.js +311 -0
  30. package/dist/chunk-B4AMLZPB.js +9 -0
  31. package/dist/chunk-BAKVJLEV.js +417 -0
  32. package/dist/chunk-BJHVULHO.js +55 -0
  33. package/dist/chunk-CB2HFKTP.js +9 -0
  34. package/dist/chunk-CE4ZOCOI.js +1640 -0
  35. package/dist/chunk-CQQL4ZOQ.js +151 -0
  36. package/dist/chunk-DEIQMBD2.js +65 -0
  37. package/dist/chunk-E6I5RXVK.js +58 -0
  38. package/dist/chunk-EFEHDQGY.js +1589 -0
  39. package/dist/chunk-EODDVJBM.js +82 -0
  40. package/dist/chunk-EPC3GZD4.js +308 -0
  41. package/dist/chunk-FAYRX2AD.js +13 -0
  42. package/dist/chunk-FFSX2QDQ.js +362 -0
  43. package/dist/chunk-FHAMQS7Q.js +5856 -0
  44. package/dist/chunk-FIV4IJL7.js +2263 -0
  45. package/dist/chunk-FNXVT7HF.js +52 -0
  46. package/dist/chunk-FSWCWSTS.js +395 -0
  47. package/dist/chunk-FW5LDDMJ.js +16464 -0
  48. package/dist/chunk-FXQLIX3K.js +211 -0
  49. package/dist/chunk-G5AAKEEZ.js +228 -0
  50. package/dist/chunk-G5S3S5ZN.js +38 -0
  51. package/dist/chunk-GAZEWQEZ.js +231 -0
  52. package/dist/chunk-GFJOMSHG.js +125 -0
  53. package/dist/chunk-GFXZTHNI.js +158 -0
  54. package/dist/chunk-GM54GJBU.js +960 -0
  55. package/dist/chunk-GPQ3NJPR.js +900 -0
  56. package/dist/chunk-GTCU6DZ3.js +1 -0
  57. package/dist/chunk-H5E77KUJ.js +15 -0
  58. package/dist/chunk-H72H4XDU.js +1023 -0
  59. package/dist/chunk-HA4ZYA6T.js +178 -0
  60. package/dist/chunk-I45KAW2N.js +5020 -0
  61. package/dist/chunk-IQ35HMQL.js +1396 -0
  62. package/dist/chunk-IYFBFX34.js +86 -0
  63. package/dist/chunk-JJPMWPB6.js +109 -0
  64. package/dist/chunk-JTHKOYWU.js +663 -0
  65. package/dist/chunk-JVGPR3TS.js +365 -0
  66. package/dist/chunk-JVKB2TBL.js +48 -0
  67. package/dist/chunk-JX7VBZDK.js +31 -0
  68. package/dist/chunk-K3G6JBUC.js +5232 -0
  69. package/dist/chunk-K4YUNKU7.js +589 -0
  70. package/dist/chunk-KBZGE6LN.js +710 -0
  71. package/dist/chunk-KDQJCT2J.js +45 -0
  72. package/dist/chunk-KI4L2DUL.js +560 -0
  73. package/dist/chunk-KJ2ZTQSL.js +47 -0
  74. package/dist/chunk-KSJ5UTP7.js +154 -0
  75. package/dist/chunk-LHOA5DWA.js +28 -0
  76. package/dist/chunk-LX5QWLBL.js +27 -0
  77. package/dist/chunk-LZDDLNCZ.js +279 -0
  78. package/dist/chunk-M2WF6AXG.js +115 -0
  79. package/dist/chunk-M7BS2K6U.js +1 -0
  80. package/dist/chunk-NDBIPFNY.js +102 -0
  81. package/dist/chunk-NEMIWEXC.js +1199 -0
  82. package/dist/chunk-NFAYHPRH.js +323 -0
  83. package/dist/chunk-O37BQ65S.js +21 -0
  84. package/dist/chunk-O74MJHXU.js +187 -0
  85. package/dist/chunk-OJZEX73F.js +112 -0
  86. package/dist/chunk-PAAWLLZX.js +74 -0
  87. package/dist/chunk-PFXFQQRT.js +339 -0
  88. package/dist/chunk-PFZO3C6H.js +14 -0
  89. package/dist/chunk-PHH7LD45.js +113 -0
  90. package/dist/chunk-QRTG4XTI.js +188 -0
  91. package/dist/chunk-QVOVMXJR.js +1351 -0
  92. package/dist/chunk-RCQYT2I4.js +757 -0
  93. package/dist/chunk-RD6DEE36.js +1178 -0
  94. package/dist/chunk-RJIZEJ42.js +30 -0
  95. package/dist/chunk-RPOA7IAE.js +150 -0
  96. package/dist/chunk-SN2DQRL6.js +1172 -0
  97. package/dist/chunk-SQJVFEBV.js +41 -0
  98. package/dist/chunk-SQP64RCS.js +5391 -0
  99. package/dist/chunk-SVT4RNTR.js +49 -0
  100. package/dist/chunk-SWUFN7XM.js +43 -0
  101. package/dist/chunk-SXMVYC5H.js +174 -0
  102. package/dist/chunk-SZ6AYUIZ.js +23 -0
  103. package/dist/chunk-T3KGZBU5.js +3171 -0
  104. package/dist/chunk-TBRL2RCC.js +9 -0
  105. package/dist/chunk-TRT2HRWZ.js +18 -0
  106. package/dist/chunk-TYW2GI5I.js +1077 -0
  107. package/dist/chunk-UIWONWVG.js +4959 -0
  108. package/dist/chunk-VB3H5WWB.js +151 -0
  109. package/dist/chunk-W4A6QA3D.js +258 -0
  110. package/dist/chunk-WBPVKEEE.js +227 -0
  111. package/dist/chunk-WEBMSQP2.js +123 -0
  112. package/dist/chunk-WOEZV3Q4.js +333 -0
  113. package/dist/chunk-WQQON3SV.js +473 -0
  114. package/dist/chunk-WQTOVMGV.js +119 -0
  115. package/dist/chunk-WZO23AWO.js +2779 -0
  116. package/dist/chunk-XBDP4QPW.js +7 -0
  117. package/dist/chunk-XEBHSJLT.js +103 -0
  118. package/dist/chunk-XQ2ECNK3.js +1 -0
  119. package/dist/chunk-XQ2XGIBF.js +2086 -0
  120. package/dist/chunk-Y2GE5TSI.js +355 -0
  121. package/dist/chunk-YBUJCZ57.js +351 -0
  122. package/dist/chunk-YNCVSFGF.js +149 -0
  123. package/dist/chunk-YUPMXOGP.js +282 -0
  124. package/dist/chunk-YY6APYUM.js +49 -0
  125. package/dist/chunk-Z2IJUO45.js +691 -0
  126. package/dist/chunk-ZEHUCJHY.js +129 -0
  127. package/dist/chunk-ZYU2YOJ6.js +1496 -0
  128. package/dist/chunk-ZZ4V3WIZ.js +163 -0
  129. package/dist/coordination/agents/index.js +145 -6260
  130. package/dist/coordination/index.js +365 -34324
  131. package/dist/coordination/mailbox-project-server.js +48 -3276
  132. package/dist/coordination/provider-status-tracker.d.ts +4 -0
  133. package/dist/core/agent.d.ts +14 -1
  134. package/dist/core/index.js +267 -12480
  135. package/dist/core/model-ref.js +5 -23
  136. package/dist/core/provider-runner.d.ts +36 -1
  137. package/dist/design/index.js +50 -1087
  138. package/dist/execution/index.d.ts +1 -1
  139. package/dist/execution/index.js +1599 -21888
  140. package/dist/execution/prompt-enhancer.js +16 -535
  141. package/dist/execution/retry-policy.d.ts +31 -1
  142. package/dist/extension/index.js +7 -311
  143. package/dist/goal/index.js +33 -3435
  144. package/dist/hooks/index.js +14 -1138
  145. package/dist/hq/index.js +112 -5139
  146. package/dist/hq/protocol.js +21 -668
  147. package/dist/index.js +1410 -92656
  148. package/dist/infrastructure/index.js +64 -4811
  149. package/dist/kernel/events/provider-events.d.ts +15 -0
  150. package/dist/kernel/index.js +14 -992
  151. package/dist/models/index.js +40 -1117
  152. package/dist/notifications/index.js +3 -108
  153. package/dist/observability/index.js +17 -279
  154. package/dist/plugin/index.js +584 -14835
  155. package/dist/prompts/index.js +14 -948
  156. package/dist/registry/index.js +9 -822
  157. package/dist/registry-R23UBE7H.js +14 -0
  158. package/dist/replay/index.js +6 -84
  159. package/dist/review-finding-store-DKAV3AS6.js +21 -0
  160. package/dist/review-report-store-OWY3EC3N.js +19 -0
  161. package/dist/security/index.js +114 -2202
  162. package/dist/session-catalog/index.js +36 -2103
  163. package/dist/session-catalog/project-server.js +42 -1435
  164. package/dist/skills/index.js +35 -1578
  165. package/dist/storage/index.js +182 -15600
  166. package/dist/tasking/index.js +10 -676
  167. package/dist/tools/index.js +52 -9590
  168. package/dist/types/config/autonomy.d.ts +5 -0
  169. package/dist/types/config/ui.d.ts +1 -1
  170. package/dist/types/errors.d.ts +18 -0
  171. package/dist/types/index.js +121 -1342
  172. package/dist/types/provider-runner.d.ts +8 -0
  173. package/dist/types/session-markers.js +8 -144
  174. package/dist/utils/error.js +3 -4
  175. package/dist/utils/expect-defined.js +3 -9
  176. package/dist/utils/heap-watchdog.js +6 -706
  177. package/dist/utils/index.js +269 -5685
  178. package/dist/utils/sage-output-block.js +5 -42
  179. package/dist/wiring/proxy-rewrite.d.ts +18 -0
  180. package/dist/wiring/proxy-rewrite.js +18 -121
  181. package/dist/worktree/index.js +6 -887
  182. 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