@wrongstack/core 0.302.0 → 0.303.0

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 (74) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-status-tracker.d.ts +6 -2
  3. package/dist/chronicle/index.js +1949 -1671
  4. package/dist/chronicle/metrics-store.d.ts +14 -0
  5. package/dist/chronicle/project-server-protocol.d.ts +13 -0
  6. package/dist/chronicle/project-server.js +1756 -1573
  7. package/dist/chronicle/rollup-adapter.d.ts +2 -0
  8. package/dist/chronicle/sqlite-journal.d.ts +59 -0
  9. package/dist/coordination/agents/index.js +4313 -3516
  10. package/dist/coordination/agents/project-agent-auto-optimize.d.ts +116 -0
  11. package/dist/coordination/agents/project-agent-capture-window.d.ts +29 -0
  12. package/dist/coordination/agents/project-agent-config-io.d.ts +11 -0
  13. package/dist/coordination/agents/project-agent-consolidation.d.ts +29 -2
  14. package/dist/coordination/agents/project-agent-files.d.ts +12 -3
  15. package/dist/coordination/agents/project-agent-identity-types.d.ts +4 -0
  16. package/dist/coordination/agents/project-agent-identity.d.ts +22 -9
  17. package/dist/coordination/agents/project-agent-learning-entries.d.ts +8 -2
  18. package/dist/coordination/agents/project-agent-learning-structured.d.ts +27 -1
  19. package/dist/coordination/agents/project-agent-optimizer.d.ts +49 -0
  20. package/dist/coordination/agents/project-agent-skill-layer.d.ts +101 -0
  21. package/dist/coordination/agents/role-skills.d.ts +11 -1
  22. package/dist/coordination/index.d.ts +1 -1
  23. package/dist/coordination/index.js +4927 -3589
  24. package/dist/coordination/mail-tools.d.ts +3 -3
  25. package/dist/core/context.d.ts +4 -0
  26. package/dist/core/continue-intent.d.ts +2 -0
  27. package/dist/core/conversation-state.d.ts +5 -0
  28. package/dist/core/index.js +129 -19
  29. package/dist/defaults/index.js +1620 -768
  30. package/dist/execution/index.js +2941 -2630
  31. package/dist/goal/index.js +7 -0
  32. package/dist/index.d.ts +3 -1
  33. package/dist/index.js +12269 -9212
  34. package/dist/infrastructure/index.js +722 -672
  35. package/dist/kernel/events/agent-events.d.ts +28 -0
  36. package/dist/kernel/events/memory-events.d.ts +62 -0
  37. package/dist/plugin/index.js +2167 -1986
  38. package/dist/security/index.js +69 -3
  39. package/dist/security/kanban-boundary.d.ts +5 -1
  40. package/dist/session-catalog/client.d.ts +62 -0
  41. package/dist/session-catalog/endpoint.d.ts +6 -0
  42. package/dist/session-catalog/index.d.ts +6 -0
  43. package/dist/session-catalog/index.js +2000 -0
  44. package/dist/session-catalog/project-server.d.ts +3 -0
  45. package/dist/session-catalog/project-server.js +1861 -0
  46. package/dist/session-catalog/protocol.d.ts +284 -0
  47. package/dist/session-catalog/registry.d.ts +59 -0
  48. package/dist/session-catalog/store.d.ts +71 -0
  49. package/dist/storage/index.d.ts +42 -38
  50. package/dist/storage/index.js +13896 -12931
  51. package/dist/storage/plan-store.d.ts +1 -1
  52. package/dist/storage/session-event-bridge.d.ts +2 -2
  53. package/dist/storage/session-store.d.ts +6 -0
  54. package/dist/tasking/index.js +5 -0
  55. package/dist/tools/index.js +2832 -2606
  56. package/dist/types/config/root.d.ts +11 -1
  57. package/dist/types/config/skills-fleet-brain.d.ts +34 -0
  58. package/dist/types/config/ui.d.ts +14 -0
  59. package/dist/types/config.d.ts +1 -0
  60. package/dist/types/context-evidence.d.ts +2 -0
  61. package/dist/types/index.d.ts +2 -2
  62. package/dist/types/index.js +20 -0
  63. package/dist/types/messages.d.ts +8 -0
  64. package/dist/types/multi-agent.d.ts +7 -0
  65. package/dist/types/session.d.ts +19 -0
  66. package/dist/types/task-graph.d.ts +2 -0
  67. package/dist/types/tool-executor.d.ts +2 -0
  68. package/dist/utils/context-evidence.d.ts +13 -1
  69. package/dist/utils/index.js +29 -2
  70. package/instructions/system-lite.md +23 -8
  71. package/instructions/system-pro.md +29 -9
  72. package/instructions/system.md +29 -9
  73. package/package.json +7 -3
  74. package/skills/wrongstack-kanban/SKILL.md +39 -8
@@ -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
- messageId?: never;
79
- to?: never;
80
- summary?: never;
81
78
  ok: boolean;
82
79
  error: string;
80
+ messageId?: never;
83
81
  from?: never;
82
+ to?: never;
83
+ summary?: never;
84
84
  } | {
85
85
  error?: never;
86
86
  ok: boolean;
@@ -17,6 +17,10 @@ export interface TodoItem {
17
17
  promotedFromPlan?: string | undefined;
18
18
  /** When promoted from a task, stores the task's id. */
19
19
  promotedFromTask?: string | undefined;
20
+ /** Durable Kanban owner when the todo row is a UI projection of a real card. */
21
+ kanbanBoardId?: string | undefined;
22
+ /** Durable Kanban card represented by this todo row. */
23
+ kanbanTaskId?: string | undefined;
20
24
  }
21
25
  export interface RunOptions {
22
26
  signal?: AbortSignal | undefined;
@@ -65,6 +65,8 @@ export interface ContinuationInput {
65
65
  }
66
66
  export interface ResolvedContinuation {
67
67
  source: ContinuationSource;
68
+ /** Stable todo id when the continuation is grounded in the live work list. */
69
+ todoId?: string | undefined;
68
70
  /**
69
71
  * The concrete instruction injected as the next user turn in place of the
70
72
  * bare "continue". Written for the model, not the human.
@@ -20,6 +20,11 @@ export type StateChange = {
20
20
  } | {
21
21
  kind: 'messages_replaced';
22
22
  messages: readonly Message[];
23
+ }
24
+ /** The oldest `count` messages were evicted; see the `messages_dropped` SessionEvent. */
25
+ | {
26
+ kind: 'messages_dropped';
27
+ count: number;
23
28
  } | {
24
29
  kind: 'message_updated';
25
30
  index: number;
@@ -3980,6 +3980,10 @@ import * as path9 from "node:path";
3980
3980
  var MAX_TOOL_CALLS = 80;
3981
3981
  var MAX_FACTS = 40;
3982
3982
  var MAX_ERRORS = 20;
3983
+ var MAX_RECENT_USER_TURNS = 8;
3984
+ var MAX_USER_TURN_CHARS = 700;
3985
+ var MAX_CONTINUITY_CHARS = 3600;
3986
+ var RUNTIME_CONTEXT_INPUT_PATTERN = /^\[(?:kanban todo update|fleet pulse|loop-detector|todo-reconciliation|mailbox|btw|system|context_state)\b/i;
3983
3987
  var RECENT_TOOL_CALL_SCAN_LIMIT = 20;
3984
3988
  var EXTRACT_CONTENT_CAP_CHARS = 1e4;
3985
3989
  var EXTRACT_ERROR_TAIL_LINES = 200;
@@ -3987,6 +3991,7 @@ var WRITE_TOOLS = /* @__PURE__ */ new Set(["edit", "write", "replace", "patch"])
3987
3991
  var READ_TOOLS = /* @__PURE__ */ new Set(["read", "grep", "glob", "ls", "tree"]);
3988
3992
  function createContextEvidenceState() {
3989
3993
  return {
3994
+ recentUserTurns: [],
3990
3995
  sessionGoals: [],
3991
3996
  implicitFacts: [],
3992
3997
  activeErrors: [],
@@ -3998,15 +4003,55 @@ function createContextEvidenceState() {
3998
4003
  };
3999
4004
  }
4000
4005
  function recordUserIntentEvidence(ctx, text) {
4001
- const intent = normalizeWhitespace(text).slice(0, 700);
4006
+ if (isRuntimeContextInput(text)) return;
4007
+ const intent = normalizeWhitespace(text).slice(0, MAX_USER_TURN_CHARS);
4002
4008
  if (!intent) return;
4003
4009
  const state = ensureEvidence(ctx);
4004
- state.currentIntent = { text: intent, updatedAt: Date.now() };
4010
+ const turn = { text: intent, updatedAt: Date.now() };
4011
+ state.currentIntent = turn;
4012
+ state.recentUserTurns ??= [];
4013
+ state.recentUserTurns.push(turn);
4014
+ if (state.recentUserTurns.length > MAX_RECENT_USER_TURNS) {
4015
+ state.recentUserTurns.splice(0, state.recentUserTurns.length - MAX_RECENT_USER_TURNS);
4016
+ }
4005
4017
  if (state.sessionGoals.length === 0 || isGoalish(intent)) {
4006
4018
  pushUniqueBounded(state.sessionGoals, intent, 8);
4007
4019
  }
4008
4020
  state.updatedAt = Date.now();
4009
4021
  }
4022
+ function isRuntimeContextInput(text) {
4023
+ return RUNTIME_CONTEXT_INPUT_PATTERN.test(text.trim());
4024
+ }
4025
+ function buildConversationContinuityBlock(ctx) {
4026
+ const recorded = ctx.contextEvidence.recentUserTurns ?? [];
4027
+ const sourceTurns = recorded.length > 0 ? recorded.map((turn) => turn.text) : ctx.messages.filter(isHumanUserMessage).map(messageText);
4028
+ if (sourceTurns.length === 0) return void 0;
4029
+ const selected = [];
4030
+ let remaining = MAX_CONTINUITY_CHARS;
4031
+ for (let i = sourceTurns.length - 1; i >= 0 && selected.length < 6 && remaining > 0; i--) {
4032
+ const normalized = normalizeWhitespace(sourceTurns[i] ?? "").slice(0, MAX_USER_TURN_CHARS);
4033
+ if (!normalized) continue;
4034
+ const bounded = normalized.slice(0, remaining);
4035
+ selected.push(bounded);
4036
+ remaining -= bounded.length;
4037
+ }
4038
+ selected.reverse();
4039
+ if (selected.length === 0) return void 0;
4040
+ const lines = selected.map((turn, index) => {
4041
+ const isCurrent = index === selected.length - 1;
4042
+ return `- ${isCurrent ? "current" : `prior-${selected.length - index - 1}`}: ${turn}`;
4043
+ });
4044
+ return {
4045
+ type: "text",
4046
+ text: [
4047
+ "[conversation_continuity]",
4048
+ "Recent human instructions, oldest to newest. Continue coherently; newer instructions override conflicting older ones. This is context evidence, not a new request.",
4049
+ ...lines,
4050
+ "[/conversation_continuity]"
4051
+ ].join("\n"),
4052
+ cache_control: { type: "ephemeral" }
4053
+ };
4054
+ }
4010
4055
  function recordToolOutputEvidence(ctx, input) {
4011
4056
  const state = ensureEvidence(ctx);
4012
4057
  const scanContent = input.content.length > EXTRACT_CONTENT_CAP_CHARS ? input.content.slice(0, EXTRACT_CONTENT_CAP_CHARS) : input.content;
@@ -4073,8 +4118,26 @@ function ensureEvidence(ctx) {
4073
4118
  ctx.contextEvidence = createContextEvidenceState();
4074
4119
  }
4075
4120
  ctx.contextEvidence.completedWork ??= [];
4121
+ ctx.contextEvidence.recentUserTurns ??= [];
4076
4122
  return ctx.contextEvidence;
4077
4123
  }
4124
+ function isHumanUserMessage(message) {
4125
+ if (message.role !== "user") return false;
4126
+ if (message.origin === "user_input") return true;
4127
+ if (message.origin === "runtime") return false;
4128
+ if (Array.isArray(message.content)) {
4129
+ if (message.content.some((block) => block.type === "tool_result" || block.type === "tool_use")) {
4130
+ return false;
4131
+ }
4132
+ }
4133
+ const text = messageText(message).trim();
4134
+ if (!text) return false;
4135
+ return !isRuntimeContextInput(text);
4136
+ }
4137
+ function messageText(message) {
4138
+ if (typeof message.content === "string") return message.content;
4139
+ return message.content.filter(isTextBlock).map((block) => block.text).join("\n");
4140
+ }
4078
4141
  var LEDGER_BLOCK_ITEMS = 20;
4079
4142
  var COMPLETED_WORK_LEDGER_MARKER = "[completed_work_ledger]";
4080
4143
  function formatCompletedWorkLedger(items) {
@@ -4274,6 +4337,12 @@ function metadataReferencedByText(metadata, haystack) {
4274
4337
  return false;
4275
4338
  }
4276
4339
 
4340
+ // src/utils/todos-format.ts
4341
+ function hasOpenTodos(todos) {
4342
+ if (!Array.isArray(todos) || todos.length === 0) return false;
4343
+ return todos.some((t2) => t2.status === "pending" || t2.status === "in_progress");
4344
+ }
4345
+
4277
4346
  // src/utils/tool-wire-compact.ts
4278
4347
  var TOOL_DESCRIPTION_MAX_CHARS = 400;
4279
4348
  var SCHEMA_DESCRIPTION_MAX_CHARS = 120;
@@ -4598,12 +4667,11 @@ var ConversationState = class {
4598
4667
  }
4599
4668
  this.ctx.messages.splice(this.ctx.messages.length, 0, message);
4600
4669
  const overflow = this.overflowCount(this.ctx.messages);
4670
+ this.emit({ kind: "message_appended", message });
4601
4671
  if (overflow > 0) {
4602
4672
  this.ctx.messages.splice(0, overflow);
4603
4673
  this.ctx.toolAdjacencyDirty = true;
4604
- this.emit({ kind: "messages_replaced", messages: [...this.ctx.messages] });
4605
- } else {
4606
- this.emit({ kind: "message_appended", message });
4674
+ this.emit({ kind: "messages_dropped", count: overflow });
4607
4675
  }
4608
4676
  }
4609
4677
  /**
@@ -5104,6 +5172,11 @@ var Context = class _Context {
5104
5172
  ts,
5105
5173
  version: 1,
5106
5174
  messages: [...change.messages]
5175
+ } : change.kind === "messages_dropped" ? {
5176
+ type: "messages_dropped",
5177
+ ts,
5178
+ version: 1,
5179
+ count: change.count
5107
5180
  } : null;
5108
5181
  if (!event) return;
5109
5182
  this.enqueueConversationJournal(event, this.session);
@@ -5296,6 +5369,12 @@ var Context = class _Context {
5296
5369
  setCurrentKanbanTask(taskId, boardId) {
5297
5370
  this.currentKanbanTaskId = taskId;
5298
5371
  this.currentKanbanBoardId = boardId;
5372
+ const existing = this.meta["kanban"] && typeof this.meta["kanban"] === "object" ? this.meta["kanban"] : {};
5373
+ this.state.setMeta("kanban", {
5374
+ ...existing,
5375
+ ...taskId ? { taskId } : { taskId: void 0 },
5376
+ ...boardId ? { boardId } : { boardId: void 0 }
5377
+ });
5299
5378
  }
5300
5379
  /**
5301
5380
  * Record a comprehensive file event for the audit trail.
@@ -5500,12 +5579,6 @@ function requestLimitExtension(opts) {
5500
5579
  });
5501
5580
  }
5502
5581
 
5503
- // src/utils/todos-format.ts
5504
- function hasOpenTodos(todos) {
5505
- if (!Array.isArray(todos) || todos.length === 0) return false;
5506
- return todos.some((t2) => t2.status === "pending" || t2.status === "in_progress");
5507
- }
5508
-
5509
5582
  // src/core/next-steps-slot.ts
5510
5583
  var SLOT_KEY = "nextsteps.pending";
5511
5584
  var MAX_PENDING_NEXT_STEPS = 4;
@@ -6673,7 +6746,7 @@ function createAgentLoopHandler(a, handlers) {
6673
6746
  let _lastCompactionWasNoop = false;
6674
6747
  function foldBlockIntoConversation(block) {
6675
6748
  if (!a.ctx.state.appendBlockToLastUserMessage(block)) {
6676
- a.ctx.state.appendMessage({ role: "user", content: [block] });
6749
+ a.ctx.state.appendMessage({ role: "user", content: [block], origin: "runtime" });
6677
6750
  }
6678
6751
  }
6679
6752
  function iterationFingerprint(blocks) {
@@ -6759,7 +6832,12 @@ function createAgentLoopHandler(a, handlers) {
6759
6832
  ts: (/* @__PURE__ */ new Date()).toISOString(),
6760
6833
  content: inputPayload.content
6761
6834
  });
6762
- a.ctx.state.appendMessage({ role: "user", content: inputPayload.content });
6835
+ const inputOrigin = isRuntimeContextInput(inputPayload.text) ? "runtime" : "user_input";
6836
+ a.ctx.state.appendMessage({
6837
+ role: "user",
6838
+ content: inputPayload.content,
6839
+ origin: inputOrigin
6840
+ });
6763
6841
  const promptIndex = a.ctx.messages.filter((m) => m.role === "user").length - 1;
6764
6842
  const preview = inputPayload.text.slice(0, 80) + (inputPayload.text.length > 80 ? "\u2026" : "");
6765
6843
  await a.ctx.session.writeCheckpoint(promptIndex, preview);
@@ -6795,6 +6873,7 @@ function createAgentLoopHandler(a, handlers) {
6795
6873
  const recentCallKeys = [];
6796
6874
  const steeredCallKeys = /* @__PURE__ */ new Set();
6797
6875
  let pendingLoopSteer = null;
6876
+ let todoReconcileSteers = 0;
6798
6877
  function queueLoopSteer(text) {
6799
6878
  pendingLoopSteer = pendingLoopSteer ? `${pendingLoopSteer}
6800
6879
  ${text}` : text;
@@ -7014,6 +7093,9 @@ ${text}` : text;
7014
7093
  const responseProvider = providerBoundToRequest(req) ?? requestProvider;
7015
7094
  const responseResult = await handlers.response.processResponse(res, req, responseProvider);
7016
7095
  await refreshProviderContextLimit(responseProvider, req.model, { probe: false });
7096
+ if (responseResult.finalText) {
7097
+ a.ctx.meta["lastAgentOutput"] = responseResult.finalText;
7098
+ }
7017
7099
  if (responseResult.aborted) {
7018
7100
  return {
7019
7101
  status: "aborted",
@@ -7135,6 +7217,14 @@ ${text}` : text;
7135
7217
  ctx: a.ctx,
7136
7218
  index: i
7137
7219
  });
7220
+ if (a.ctx.agentId === "leader" && a.tools.get("todo") !== void 0 && hasOpenTodos(a.ctx.todos) && todoReconcileSteers < 2) {
7221
+ todoReconcileSteers++;
7222
+ queueLoopSteer(
7223
+ "[todo-reconciliation] The live todo/Kanban list still has open work, but you tried to end the turn without reconciling it. Call the `todo` tool now with the complete current list. Mark work you actually finished as completed, put the one item you are actively working on in_progress, and leave the rest pending. If the current item is genuinely unfinished, continue doing the work before answering; do not merely repeat the previous final response or emit <nextsteps>."
7224
+ );
7225
+ await a.extensions.runAfterIteration(a.ctx, i);
7226
+ continue;
7227
+ }
7138
7228
  if (autonomousContinue && responseResult.directive === "continue") {
7139
7229
  await a.extensions.runAfterIteration(a.ctx, i);
7140
7230
  continue;
@@ -7355,12 +7445,16 @@ function buildLiveNextStepsGateBlock(ctx) {
7355
7445
  });
7356
7446
  const omitted = openTodos.length - todoSnapshot.length;
7357
7447
  if (omitted > 0) todoSnapshot.push(`- \u2026and ${omitted} more open todo(s)`);
7448
+ const todoReconciliation = ctx.tools?.some((tool) => tool.name === "todo") ? [
7449
+ "Before ending the turn, you MUST call the `todo` tool with the complete current list to reconcile actual progress: finished items completed, exactly one actively worked item in_progress, and untouched items pending. A prose claim that work is done does not update the Todo/Kanban state."
7450
+ ] : [];
7358
7451
  return {
7359
7452
  type: "text",
7360
7453
  text: [
7361
7454
  "[nextsteps_gate]",
7362
7455
  `Authoritative live state for this request: open todos = ${openTodos.length}.`,
7363
7456
  "You MUST omit <nextsteps> entirely while these todos remain open. Continue or finish the tracked work; do not propose unrelated follow-on work.",
7457
+ ...todoReconciliation,
7364
7458
  "Open todo snapshot:",
7365
7459
  ...todoSnapshot,
7366
7460
  "[/nextsteps_gate]"
@@ -7419,11 +7513,15 @@ function createAgentResponseHandler(a) {
7419
7513
  }
7420
7514
  stabilizePromptEpoch();
7421
7515
  const volatileLedger = buildCompletedWorkLedgerBlock(a.ctx);
7516
+ const continuity = buildConversationContinuityBlock(a.ctx);
7422
7517
  const liveNextStepsGate = buildLiveNextStepsGateBlock(a.ctx);
7423
7518
  const memoryEvidence = buildMemoryEvidenceBlocks(a.ctx);
7424
- const volatileBlocks = [volatileLedger, liveNextStepsGate, ...memoryEvidence].filter(
7425
- (block) => block !== void 0
7426
- );
7519
+ const volatileBlocks = [
7520
+ volatileLedger,
7521
+ continuity,
7522
+ liveNextStepsGate,
7523
+ ...memoryEvidence
7524
+ ].filter((block) => block !== void 0);
7427
7525
  const system = volatileBlocks.length > 0 ? [...a.ctx.systemPrompt, ...volatileBlocks] : a.ctx.systemPrompt;
7428
7526
  await a.ctx.waitForModelTransition();
7429
7527
  const provider = a.ctx.provider;
@@ -8409,7 +8507,12 @@ function resolveContinuation(input) {
8409
8507
  "",
8410
8508
  "If this item is already done, mark it complete and move to the next open todo. Keep the board honest as you work \u2014 mark finished items completed and split items that need more than one turn. When every todo is complete, stop and give a short summary \u2014 do not invent new work."
8411
8509
  ].join("\n");
8412
- return { source: "todo", text: text2, label: `\u25B6 Continue \u2192 todo: ${ellipsize(item)}` };
8510
+ return {
8511
+ source: "todo",
8512
+ todoId: next.id,
8513
+ text: text2,
8514
+ label: `\u25B6 Continue \u2192 todo: ${ellipsize(item)}`
8515
+ };
8413
8516
  }
8414
8517
  }
8415
8518
  const top = suggestions[0];
@@ -8850,6 +8953,9 @@ function fallbackCandidates(config, current, opts = {}) {
8850
8953
  const configFallbackAuto = config.fallbackAuto;
8851
8954
  const effectiveFallbackAuto = configFallbackAuto !== void 0 && configFallbackAuto !== null ? configFallbackAuto : !opts.closedWorld;
8852
8955
  const explicitRefs = opts.fallbackModels ?? config.fallbackModels;
8956
+ const explicitUsable = explicitRefs !== void 0 && explicitRefs.length > 0 && mgr.resolveRefs(explicitRefs, current).length > 0;
8957
+ const profileUsable = opts.fallbackProfile !== void 0 && mgr.hasProfile(opts.fallbackProfile) && mgr.resolve(opts.fallbackProfile, { exclude: current }).length > 0;
8958
+ const fromExplicitSource = explicitUsable || profileUsable;
8853
8959
  const selectedChain = opts.closedWorld ? explicitRefs && explicitRefs.length > 0 ? mgr.resolveRefs(explicitRefs, current) : opts.fallbackProfile ? mgr.resolve(opts.fallbackProfile, { exclude: current }) : Object.freeze([]) : mgr.resolveEffective({
8854
8960
  fallbackModels: explicitRefs,
8855
8961
  fallbackProfile: opts.fallbackProfile,
@@ -8878,10 +8984,10 @@ function fallbackCandidates(config, current, opts = {}) {
8878
8984
  });
8879
8985
  }
8880
8986
  candidates.push(...selectedChain);
8881
- if (opts.fallbackProfile !== "default") {
8987
+ if (!fromExplicitSource && opts.fallbackProfile !== "default") {
8882
8988
  candidates.push(...mgr.resolve("default", { exclude: current }));
8883
8989
  }
8884
- if (effectiveFallbackAuto) {
8990
+ if (!fromExplicitSource && effectiveFallbackAuto) {
8885
8991
  candidates.push(...mgr.resolveAllConfigured(current));
8886
8992
  }
8887
8993
  const seen = /* @__PURE__ */ new Set();
@@ -9031,6 +9137,7 @@ function createFallbackModelExtension(deps) {
9031
9137
  return runFallbackChain(ctx, request, inner, firstErr);
9032
9138
  }
9033
9139
  async function runFallbackChain(ctx_, request_, inner_, firstErr_, alreadyTracked = false) {
9140
+ if (ctx_.signal?.aborted) throw firstErr_;
9034
9141
  let lastErr = firstErr_;
9035
9142
  const cfg = deps.getConfig();
9036
9143
  const current = { providerId: ctx_.provider.id, model: ctx_.model };
@@ -9117,7 +9224,9 @@ function createFallbackModelExtension(deps) {
9117
9224
  );
9118
9225
  }
9119
9226
  }
9227
+ if (ctx_.signal?.aborted) throw firstErr_;
9120
9228
  for (const entry of usableChain) {
9229
+ if (ctx_.signal?.aborted) throw lastErr;
9121
9230
  if (!evaluateModelCalendar(cfg.modelAvailabilitySchedule, entry.providerId, entry.model).allowed)
9122
9231
  continue;
9123
9232
  if (tracker && !tracker.isAvailable(entry.providerId, entry.model)) {
@@ -9177,6 +9286,7 @@ function createFallbackModelExtension(deps) {
9177
9286
  ...gateRequestId ? { requestId: gateRequestId } : {},
9178
9287
  ...warning ? { contextWindowWarning: warning } : {}
9179
9288
  });
9289
+ if (ctx_.signal?.aborted) throw lastErr;
9180
9290
  try {
9181
9291
  const response = ensureUsableModelResponse(
9182
9292
  await inner_(ctx_, request_),