@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.
@@ -8328,6 +8328,24 @@ import { dirname as dirname5 } from "node:path";
8328
8328
  import { homedir as homedir2 } from "node:os";
8329
8329
 
8330
8330
  // src/security/secret-scrubber.ts
8331
+ var JSON_CREDENTIAL_KEY_ANCHORS = [
8332
+ 'Key"',
8333
+ 'key"',
8334
+ 'KEY"',
8335
+ 'token"',
8336
+ 'Token"',
8337
+ 'TOKEN"',
8338
+ 'secret"',
8339
+ 'Secret"',
8340
+ 'SECRET"',
8341
+ 'password"',
8342
+ 'Password"',
8343
+ 'PASSWORD"',
8344
+ 'authorization"',
8345
+ 'Authorization"',
8346
+ 'bearer"',
8347
+ 'Bearer"'
8348
+ ];
8331
8349
  var PATTERNS = [
8332
8350
  // Anchored at the start where possible so partial matches inside larger
8333
8351
  // strings don't trigger false positives.
@@ -8430,6 +8448,30 @@ var PATTERNS = [
8430
8448
  regex: /(^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?=\s|$)/g,
8431
8449
  anchor: ["KEY", "TOKEN", "SECRET", "PASSWORD", "PWD"]
8432
8450
  },
8451
+ {
8452
+ type: "json_credential_key",
8453
+ // The JSON counterpart to `high_entropy_env`, and the pattern that the
8454
+ // now-deleted `JSON_KEY_ANCHORS` list was written for. Without it those
8455
+ // anchors only widened the cheap pre-scan — `hasCredentialAnchors` said
8456
+ // "this text may hold a secret", every pattern then declined to match, and
8457
+ // the value went out verbatim. `high_entropy_env` cannot cover these: it
8458
+ // requires an UPPERCASE unquoted key (`API_KEY=…`), so `{"apiKey":"…"}`
8459
+ // never matched.
8460
+ //
8461
+ // Tool results are routinely serialised as JSON, and a credential with no
8462
+ // recognisable prefix (Azure, self-hosted gateways, Anthropic/Codex OAuth)
8463
+ // has no other pattern that can catch it — this is the only thing standing
8464
+ // between such a value and the session JSONL, chronicle, HQ broadcast and
8465
+ // the model's own context.
8466
+ //
8467
+ // The key may carry a prefix (`"anthropicApiKey"`), but the credential word
8468
+ // must END the key: `"tokenCount"` and `"maxTokens"` do not match, because
8469
+ // the closing quote has to follow the word immediately.
8470
+ // Value floor of 8 chars keeps enum-ish values (`"authorization":"none"`)
8471
+ // out. Capture groups: 1=key + punctuation, 2=value, 3=closing quote.
8472
+ 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,
8473
+ anchor: JSON_CREDENTIAL_KEY_ANCHORS
8474
+ },
8433
8475
  // ── Ported from packages/plugins credential-patterns.ts (WS-034) ─────────
8434
8476
  // The plugin runtime carried 37 patterns while this scrubber — the one that
8435
8477
  // guards session JSONL, chronicle, HQ broadcast, WebUI events and the auth
@@ -8512,9 +8554,12 @@ var PATTERNS = [
8512
8554
  anchor: "GOCSPX-"
8513
8555
  }
8514
8556
  ];
8515
- var SIMPLE_PATTERNS = PATTERNS.filter((p) => p.type !== "high_entropy_env");
8557
+ var SIMPLE_PATTERNS = PATTERNS.filter(
8558
+ (p) => p.type !== "high_entropy_env" && p.type !== "json_credential_key"
8559
+ );
8516
8560
  var COMBINED_REGEX = new RegExp(SIMPLE_PATTERNS.map((p) => `(${p.regex.source})`).join("|"), "g");
8517
8561
  var HIGH_ENTROPY_REGEX = PATTERNS.find((p) => p.type === "high_entropy_env").regex;
8562
+ var JSON_CREDENTIAL_REGEX = PATTERNS.find((p) => p.type === "json_credential_key").regex;
8518
8563
  var COMBINED_REPLACEMENTS = SIMPLE_PATTERNS.map((p) => `[REDACTED:${p.type}]`);
8519
8564
  var SCRUB_CHUNK_BYTES = 64 * 1024;
8520
8565
  var SCRUB_OVERLAP_BYTES = 1024;
@@ -8525,20 +8570,7 @@ var PATTERN_ANCHORS = [
8525
8570
  )
8526
8571
  )
8527
8572
  ];
8528
- var JSON_KEY_ANCHORS = [
8529
- '"apiKey"',
8530
- '"api_key"',
8531
- '"token"',
8532
- '"secret"',
8533
- '"password"',
8534
- '"authorization"',
8535
- '"bearer"',
8536
- '"private_key"',
8537
- '"access_token"',
8538
- '"refresh_token"',
8539
- '"client_secret"'
8540
- ];
8541
- var ALL_ANCHORS = [...PATTERN_ANCHORS, ...JSON_KEY_ANCHORS];
8573
+ var ALL_ANCHORS = PATTERN_ANCHORS;
8542
8574
  function hasCredentialAnchors(text) {
8543
8575
  for (const anchor of ALL_ANCHORS) {
8544
8576
  if (text.includes(anchor)) return true;
@@ -8587,6 +8619,9 @@ var DefaultSecretScrubber = class {
8587
8619
  out = out.replace(HIGH_ENTROPY_REGEX, (_match, lead, key, _value) => {
8588
8620
  return `${lead}${key}=[REDACTED:high_entropy_env]`;
8589
8621
  });
8622
+ out = out.replace(JSON_CREDENTIAL_REGEX, (_match, keyPrefix, _value, closingQuote) => {
8623
+ return `${keyPrefix}[REDACTED:json_credential_key]${closingQuote}`;
8624
+ });
8590
8625
  return out;
8591
8626
  }
8592
8627
  /**
@@ -17102,9 +17137,40 @@ async function readOrBuildShardManifestEntry(opts) {
17102
17137
  return entry;
17103
17138
  }
17104
17139
 
17105
- // src/storage/session-store/summary-builder.ts
17140
+ // src/storage/session-store/strict-empty-check.ts
17106
17141
  import { createReadStream as createReadStream4 } from "node:fs";
17107
17142
  import { createInterface as createInterface4 } from "node:readline";
17143
+ var EMPTY_SESSION_EVENT_TYPES = /* @__PURE__ */ new Set(["session_start", "session_resumed", "session_end"]);
17144
+ async function isStrictlyEmptySessionFile(file) {
17145
+ const input = createReadStream4(file, { encoding: "utf8" });
17146
+ const lines = createInterface4({ input, crlfDelay: Infinity });
17147
+ let sawSessionStart = false;
17148
+ try {
17149
+ for await (const line of lines) {
17150
+ if (!line.trim()) continue;
17151
+ let event;
17152
+ try {
17153
+ event = JSON.parse(line);
17154
+ } catch {
17155
+ return false;
17156
+ }
17157
+ if (event === null || typeof event !== "object" || Array.isArray(event)) return false;
17158
+ const type = event.type;
17159
+ if (typeof type !== "string" || !EMPTY_SESSION_EVENT_TYPES.has(type)) return false;
17160
+ if (type === "session_start") sawSessionStart = true;
17161
+ }
17162
+ } catch {
17163
+ return false;
17164
+ } finally {
17165
+ lines.close();
17166
+ input.destroy();
17167
+ }
17168
+ return sawSessionStart;
17169
+ }
17170
+
17171
+ // src/storage/session-store/summary-builder.ts
17172
+ import { createReadStream as createReadStream5 } from "node:fs";
17173
+ import { createInterface as createInterface5 } from "node:readline";
17108
17174
  async function summarizeSessionFile(opts) {
17109
17175
  return summarizeSessionEventSequence({
17110
17176
  id: opts.id,
@@ -17221,8 +17287,8 @@ async function summarizeSessionEventSequence(opts) {
17221
17287
  }
17222
17288
  }
17223
17289
  async function* iterateSessionEvents(file, secretScrubber) {
17224
- const stream = createReadStream4(file, { encoding: "utf8" });
17225
- const lines = createInterface4({ input: stream, crlfDelay: Infinity });
17290
+ const stream = createReadStream5(file, { encoding: "utf8" });
17291
+ const lines = createInterface5({ input: stream, crlfDelay: Infinity });
17226
17292
  try {
17227
17293
  for await (const line of lines) {
17228
17294
  if (!line.trim()) continue;
@@ -18118,6 +18184,10 @@ var DefaultSessionStore = class _DefaultSessionStore {
18118
18184
  await deleteSessionArtifacts({ rootDir: this.dir, id, jsonlPath });
18119
18185
  await this.writeTombstone(id);
18120
18186
  }
18187
+ async isEmpty(id) {
18188
+ const canonicalId = await this.resolveId(id);
18189
+ return isStrictlyEmptySessionFile(this.sessionPath(canonicalId, ".jsonl"));
18190
+ }
18121
18191
  async delete(id) {
18122
18192
  if (this.catalogClient) {
18123
18193
  const canonical = await this.resolveId(id);
@@ -18681,6 +18751,12 @@ function parseModelRef(ref) {
18681
18751
  function hasText(value) {
18682
18752
  return typeof value === "string" && value.trim().length > 0;
18683
18753
  }
18754
+ function asRefList(value) {
18755
+ return Array.isArray(value) ? value : void 0;
18756
+ }
18757
+ function asProfileName(value) {
18758
+ return hasText(value) ? value : void 0;
18759
+ }
18684
18760
  function providerHasKey(entry) {
18685
18761
  if (!entry) return false;
18686
18762
  if (hasText(entry.apiKey)) return true;
@@ -18691,7 +18767,7 @@ function providerHasKey(entry) {
18691
18767
  }
18692
18768
  function visibleProviderModels(config, providerId, providerModels) {
18693
18769
  const entry = config.providers?.[providerId];
18694
- return entry?.models !== void 0 ? [...entry.models] : providerModels;
18770
+ return Array.isArray(entry?.models) ? [...entry.models] : providerModels;
18695
18771
  }
18696
18772
  function buildProfiles(config) {
18697
18773
  const entries = /* @__PURE__ */ new Map();
@@ -18728,13 +18804,34 @@ var FallbackProfileManager = class {
18728
18804
  listProfiles() {
18729
18805
  return Object.freeze([...this.profiles.keys()]);
18730
18806
  }
18807
+ /**
18808
+ * The profile the session has selected (`config.fallbackProfile`, set by
18809
+ * `/fallback profile use <name>`), or undefined when none is selected or the
18810
+ * name no longer resolves to a defined profile.
18811
+ *
18812
+ * Consulted by every resolution entry point when the caller does not name a
18813
+ * profile itself. Without this the leader — which passes no profile — could
18814
+ * never use a named profile at all: `config.fallbackProfiles` was reachable
18815
+ * only by copying a chain into `fallbackModels`.
18816
+ */
18817
+ activeProfileName() {
18818
+ const name = asProfileName(this.config.fallbackProfile);
18819
+ return name && this.profiles.has(name) ? name : void 0;
18820
+ }
18731
18821
  // ── Resolution ─────────────────────────────────────────────────────────
18732
18822
  /**
18733
18823
  * Resolve a named fallback profile to a validated, provider-filtered chain.
18734
18824
  *
18735
- * Returns an empty chain when:
18736
- * - The profile doesn't exist.
18737
- * - Every entry's provider is missing, has no key, or has no matching model.
18825
+ * Returns an empty chain when the profile doesn't exist, or when every entry
18826
+ * is excluded, quarantined, or blacked out.
18827
+ *
18828
+ * Filtering is intentionally identical to {@link resolveRefs} (the explicit
18829
+ * `fallbackModels` path): the self-exclusion, the runtime status tracker,
18830
+ * and the availability calendar — nothing else. Anything a named profile
18831
+ * drops, an explicit chain drops too, and vice versa. Profiles used to apply
18832
+ * two extra filters (provider "usability" and the `providers[].models`
18833
+ * snapshot) that the explicit path did not, which silently rerouted roles
18834
+ * pinned to a profile onto a different model than the one configured.
18738
18835
  *
18739
18836
  * @param name - Profile name from config.fallbackProfiles.
18740
18837
  * @param defaultProvider - Used when an entry has no explicit provider.
@@ -18755,13 +18852,9 @@ var FallbackProfileManager = class {
18755
18852
  if (seen.has(key)) continue;
18756
18853
  seen.add(key);
18757
18854
  if (excludeKey && key === excludeKey) continue;
18758
- const health = this.checkProvider(providerId);
18759
- if (!health.usable) continue;
18760
18855
  if (this.statusTracker && !this.statusTracker.isAvailable(providerId, parsed.model)) continue;
18761
18856
  if (!evaluateModelCalendar(this.config.modelAvailabilitySchedule, providerId, parsed.model).allowed)
18762
18857
  continue;
18763
- const allowedModels = this.config.providers?.[providerId]?.models;
18764
- if (allowedModels && !allowedModels.includes(parsed.model)) continue;
18765
18858
  resolved.push({
18766
18859
  providerId,
18767
18860
  model: parsed.model,
@@ -18779,12 +18872,14 @@ var FallbackProfileManager = class {
18779
18872
  resolveEffective(opts = {}) {
18780
18873
  const bridge = this.resolveBridge(opts.exclude);
18781
18874
  let selected = FREEZER_EMPTY;
18782
- if (opts.fallbackModels && opts.fallbackModels.length > 0) {
18783
- const resolved = this.resolveRefs(opts.fallbackModels, opts.exclude);
18875
+ const explicitRefs = asRefList(opts.fallbackModels);
18876
+ const profileName = asProfileName(opts.fallbackProfile) ?? this.activeProfileName();
18877
+ if (explicitRefs && explicitRefs.length > 0) {
18878
+ const resolved = this.resolveRefs(explicitRefs, opts.exclude);
18784
18879
  if (resolved.length > 0) selected = resolved;
18785
18880
  }
18786
- if (selected.length === 0 && opts.fallbackProfile) {
18787
- const resolved = this.resolve(opts.fallbackProfile, { exclude: opts.exclude });
18881
+ if (selected.length === 0 && profileName) {
18882
+ const resolved = this.resolve(profileName, { exclude: opts.exclude });
18788
18883
  if (resolved.length > 0) selected = resolved;
18789
18884
  }
18790
18885
  if (selected.length === 0 && opts.fallbackAuto !== false) {
@@ -18860,13 +18955,14 @@ var FallbackProfileManager = class {
18860
18955
  };
18861
18956
  const configFallbackAuto = this.config.fallbackAuto;
18862
18957
  const effectiveFallbackAuto = configFallbackAuto !== void 0 && configFallbackAuto !== null ? configFallbackAuto : !opts.closedWorld;
18863
- const explicitRefs = opts.fallbackModels ?? this.config.fallbackModels;
18958
+ const explicitRefs = asRefList(opts.fallbackModels) ?? asRefList(this.config.fallbackModels);
18959
+ const profileName = asProfileName(opts.fallbackProfile) ?? this.activeProfileName();
18864
18960
  const explicitUsable = explicitRefs !== void 0 && explicitRefs.length > 0 && this.resolveRefs(explicitRefs, current).length > 0;
18865
- const profileUsable = opts.fallbackProfile !== void 0 && this.hasProfile(opts.fallbackProfile) && this.resolve(opts.fallbackProfile, { exclude: current }).length > 0;
18961
+ const profileUsable = profileName !== void 0 && this.hasProfile(profileName) && this.resolve(profileName, { exclude: current }).length > 0;
18866
18962
  const fromExplicitSource = explicitUsable || profileUsable;
18867
- const selectedChain = opts.closedWorld ? explicitRefs && explicitRefs.length > 0 ? this.resolveRefs(explicitRefs, current) : opts.fallbackProfile ? this.resolve(opts.fallbackProfile, { exclude: current }) : FREEZER_EMPTY : this.resolveEffective({
18963
+ const selectedChain = opts.closedWorld ? explicitRefs && explicitRefs.length > 0 ? this.resolveRefs(explicitRefs, current) : profileName ? this.resolve(profileName, { exclude: current }) : FREEZER_EMPTY : this.resolveEffective({
18868
18964
  fallbackModels: explicitRefs,
18869
- fallbackProfile: opts.fallbackProfile,
18965
+ fallbackProfile: profileName,
18870
18966
  fallbackAuto: effectiveFallbackAuto,
18871
18967
  exclude: current
18872
18968
  });
@@ -18883,7 +18979,7 @@ var FallbackProfileManager = class {
18883
18979
  });
18884
18980
  }
18885
18981
  candidates.push(...selectedChain);
18886
- if (!fromExplicitSource && effectiveFallbackAuto && opts.fallbackProfile !== "default") {
18982
+ if (!fromExplicitSource && effectiveFallbackAuto && profileName !== "default") {
18887
18983
  candidates.push(...this.resolve("default", { exclude: current }));
18888
18984
  }
18889
18985
  if (!fromExplicitSource && effectiveFallbackAuto) {
@@ -18961,7 +19057,7 @@ var FallbackProfileManager = class {
18961
19057
  const leaderModel = this.config.model;
18962
19058
  const providers = this.config.providers ?? {};
18963
19059
  const favoriteSet = new Set(
18964
- (this.config.favoriteModels ?? []).map((ref) => {
19060
+ (asRefList(this.config.favoriteModels) ?? []).map((ref) => {
18965
19061
  const p = parseModelRef(ref);
18966
19062
  return `${p.provider ?? leaderProvider}/${p.model}`;
18967
19063
  })
@@ -27720,10 +27816,10 @@ var AdaptiveConcurrencyController = class {
27720
27816
  };
27721
27817
 
27722
27818
  // src/coordination/agent-monitor.ts
27723
- import { createReadStream as createReadStream5 } from "node:fs";
27819
+ import { createReadStream as createReadStream6 } from "node:fs";
27724
27820
  import * as fs6 from "node:fs/promises";
27725
27821
  import * as path38 from "node:path";
27726
- import { createInterface as createInterface5 } from "node:readline";
27822
+ import { createInterface as createInterface6 } from "node:readline";
27727
27823
  var AgentMonitorService = class _AgentMonitorService {
27728
27824
  _fleetBus;
27729
27825
  _events;
@@ -27830,8 +27926,8 @@ var AgentMonitorService = class _AgentMonitorService {
27830
27926
  const accessible = await fs6.access(file).then(() => true).catch(() => false);
27831
27927
  if (!accessible) return [];
27832
27928
  const out = [];
27833
- const input = createReadStream5(file, { encoding: "utf8" });
27834
- const lines = createInterface5({ input, crlfDelay: Number.POSITIVE_INFINITY });
27929
+ const input = createReadStream6(file, { encoding: "utf8" });
27930
+ const lines = createInterface6({ input, crlfDelay: Number.POSITIVE_INFINITY });
27835
27931
  try {
27836
27932
  for await (const line of lines) {
27837
27933
  const trimmed = line.trim();
@@ -75,12 +75,12 @@ export declare function makeMailSendTool(opts?: MailToolsOptions): {
75
75
  required: string[];
76
76
  };
77
77
  execute(input: unknown, ctx: Context): Promise<{
78
+ summary?: never;
78
79
  ok: boolean;
79
80
  error: string;
80
81
  messageId?: never;
81
82
  from?: never;
82
83
  to?: never;
83
- summary?: never;
84
84
  } | {
85
85
  error?: never;
86
86
  ok: boolean;
@@ -1,7 +1,6 @@
1
1
  import type { ContentBlock } from '../types/blocks.js';
2
2
  import type { Message } from '../types/messages.js';
3
- import type { TodoItem } from './context.js';
4
- import { Context } from './context.js';
3
+ import type { Context, TodoItem } from './context.js';
5
4
  /**
6
5
  * Observable wrapper for mutable conversation state. Production code should
7
6
  * mutate messages, todos, and meta through this API so subscribers see a
@@ -143,9 +143,27 @@ export declare function fallbackProfileChain(config: Config, profileName: string
143
143
  export declare function smartDefaultFallbackChain(config: Config): string[];
144
144
  /**
145
145
  * The effective fallback chain for a turn: the explicit `fallbackModels` list
146
- * when non-empty, otherwise the smart default (unless `fallbackAuto` is off).
146
+ * when non-empty, otherwise the selected profile, otherwise the smart default
147
+ * (unless `fallbackAuto` is off).
148
+ *
149
+ * NOTE: this is the SELECTED chain, not the full runtime order — it omits the
150
+ * bridge, the primary re-insertion, the extra `default`-profile depth and the
151
+ * last-resort sweep that {@link runtimeFallbackChain} adds. Use
152
+ * `runtimeFallbackChain` for anything shown to a user as "what will be tried".
147
153
  */
148
154
  export declare function effectiveFallbackChain(config: Config): string[];
155
+ /**
156
+ * The chain the agent loop will ACTUALLY rotate through, in order, if the
157
+ * current primary fails right now — the same `resolveCandidates` call the
158
+ * fallback extension makes, including bridge, primary re-insertion, the
159
+ * `default`-profile depth and the last-resort sweep.
160
+ *
161
+ * `/fallback` used to render `effectiveFallbackChain` instead, so the
162
+ * displayed chain could be four entries while the runtime rotated through
163
+ * seventeen — the view was structurally unable to match the behavior it
164
+ * claimed to describe.
165
+ */
166
+ export declare function runtimeFallbackChain(config: Config): string[];
149
167
  /**
150
168
  * Build the cross-provider fallback extension. Always returns an extension —
151
169
  * the effective chain (`effectiveFallbackChain`) is recomputed every turn from
@@ -59,12 +59,30 @@ export declare class FallbackProfileManager {
59
59
  setStatusTracker(tracker: ProviderModelStatusTracker | undefined): void;
60
60
  hasProfile(name: string): boolean;
61
61
  listProfiles(): readonly string[];
62
+ /**
63
+ * The profile the session has selected (`config.fallbackProfile`, set by
64
+ * `/fallback profile use <name>`), or undefined when none is selected or the
65
+ * name no longer resolves to a defined profile.
66
+ *
67
+ * Consulted by every resolution entry point when the caller does not name a
68
+ * profile itself. Without this the leader — which passes no profile — could
69
+ * never use a named profile at all: `config.fallbackProfiles` was reachable
70
+ * only by copying a chain into `fallbackModels`.
71
+ */
72
+ activeProfileName(): string | undefined;
62
73
  /**
63
74
  * Resolve a named fallback profile to a validated, provider-filtered chain.
64
75
  *
65
- * Returns an empty chain when:
66
- * - The profile doesn't exist.
67
- * - Every entry's provider is missing, has no key, or has no matching model.
76
+ * Returns an empty chain when the profile doesn't exist, or when every entry
77
+ * is excluded, quarantined, or blacked out.
78
+ *
79
+ * Filtering is intentionally identical to {@link resolveRefs} (the explicit
80
+ * `fallbackModels` path): the self-exclusion, the runtime status tracker,
81
+ * and the availability calendar — nothing else. Anything a named profile
82
+ * drops, an explicit chain drops too, and vice versa. Profiles used to apply
83
+ * two extra filters (provider "usability" and the `providers[].models`
84
+ * snapshot) that the explicit path did not, which silently rerouted roles
85
+ * pinned to a profile onto a different model than the one configured.
68
86
  *
69
87
  * @param name - Profile name from config.fallbackProfiles.
70
88
  * @param defaultProvider - Used when an entry has no explicit provider.
@@ -4,7 +4,7 @@ export { buildBtwBlock, consumeBtwNotes, pendingBtwCount, setBtwNote, } from './
4
4
  export { Context, type ContextInit, type ProviderMemoryEvidence, type RunOptions, type TodoItem, } from './context.js';
5
5
  export { type ContinuationInput, type ContinuationSource, detectContinueIntent, type ResolvedContinuation, resolveContinuation, } from './continue-intent.js';
6
6
  export { ConversationState, type ReadonlyConversationState, type StateChange, type StateChangeHandler, wrapAsState, } from './conversation-state.js';
7
- export { createFallbackModelExtension, effectiveFallbackChain, type FallbackGateFn, type FallbackModelDeps, fallbackProfileChain, smartDefaultFallbackChain, } from './fallback-model.js';
7
+ export { createFallbackModelExtension, effectiveFallbackChain, type FallbackGateFn, type FallbackModelDeps, fallbackProfileChain, runtimeFallbackChain, smartDefaultFallbackChain, } from './fallback-model.js';
8
8
  export { FallbackProfileManager } from './fallback-profile-manager.js';
9
9
  export { InputBuilder, type InputBuilderEvent, type InputBuilderOptions, } from './input-builder.js';
10
10
  export { type InstructionBundle, type InstructionBundlePaths, loadInstructionBundle, type SystemInstructionVariant, } from './instruction-bundle.js';