@ouro.bot/cli 0.1.0-alpha.351 → 0.1.0-alpha.353

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/changelog.json CHANGED
@@ -1,6 +1,20 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.353",
6
+ "changes": [
7
+ "Provider failover now uses the machine-wide credential pool and local provider-state lanes: terminal errors build ready-provider choices with safe credential provenance, `switch to <provider>` carries lane/model/revision context, and the retry path updates only the failed `state/providers.json` lane instead of mutating both synced `agent.json` facings."
8
+ ]
9
+ },
10
+ {
11
+ "version": "0.1.0-alpha.352",
12
+ "changes": [
13
+ "Settle tool description now communicates turn-ending semantics: 'deliver your response and end your turn' with explicit guidance against settling with status updates mid-task.",
14
+ "Observe tool now available in all outward channels including 1:1 chats, not just groups and reactions — agents can absorb messages without responding when the moment doesn't call for words.",
15
+ "Autonomous execution prompt contract added: when told to work autonomously, agents use ponder to absorb new messages and continue using tools, settling only with the final result."
16
+ ]
17
+ },
4
18
  {
5
19
  "version": "0.1.0-alpha.351",
6
20
  "changes": [
@@ -556,8 +556,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
556
556
  while (!done) {
557
557
  // Channel-based tool filtering:
558
558
  // - Inner dialog: exclude send_message (delivery via surface), observe (no one to observe)
559
- // - 1:1 sessions: exclude observe (can't ignore someone talking directly to you)
560
- // - Group chats: observe available
559
+ // - All outward channels (1:1, group, reaction): observe available
561
560
  //
562
561
  // ponder, settle/rest, surface, and observe are always assembled based on channel context.
563
562
  // ponder is available in ALL channels (outer: think privately, inner: keep turning).
@@ -571,7 +570,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
571
570
  ...filteredBaseTools,
572
571
  tools_1.ponderTool,
573
572
  ...(isInnerDialog ? [tools_2.surfaceToolDef, tools_1.restTool] : []),
574
- ...((currentContext?.isGroupChat || options?.isReactionSignal) && !isInnerDialog ? [tools_1.observeTool] : []),
573
+ ...(!isInnerDialog ? [tools_1.observeTool] : []),
575
574
  ...(!isInnerDialog ? [tools_1.settleTool] : []),
576
575
  ];
577
576
  const steeringFollowUps = options?.drainSteeringFollowUps?.() ?? [];
@@ -1,8 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatCredentialProvenanceLabel = formatCredentialProvenanceLabel;
4
+ exports.formatReadyProviderLabel = formatReadyProviderLabel;
3
5
  exports.buildFailoverContext = buildFailoverContext;
4
6
  exports.handleFailoverReply = handleFailoverReply;
7
+ exports.runMachineProviderFailoverInventory = runMachineProviderFailoverInventory;
8
+ const identity_1 = require("./identity");
9
+ const provider_ping_1 = require("./provider-ping");
5
10
  const provider_models_1 = require("./provider-models");
11
+ const provider_credential_pool_1 = require("./provider-credential-pool");
6
12
  const runtime_1 = require("../nerves/runtime");
7
13
  const CLASSIFICATION_LABELS = {
8
14
  "auth-failure": "authentication failed",
@@ -26,6 +32,23 @@ function formatErrorDetail(errorMessage, errorSummary) {
26
32
  return "";
27
33
  return detail.length > 300 ? `${detail.slice(0, 297)}...` : detail;
28
34
  }
35
+ function formatCredentialProvenanceLabel(candidate) {
36
+ if (candidate.contributedByAgent && candidate.source) {
37
+ return `credentials from ${candidate.contributedByAgent} via ${candidate.source}`;
38
+ }
39
+ if (candidate.contributedByAgent) {
40
+ return `credentials from ${candidate.contributedByAgent}`;
41
+ }
42
+ if (candidate.source) {
43
+ return `credentials from this machine via ${candidate.source}`;
44
+ }
45
+ return undefined;
46
+ }
47
+ function formatReadyProviderLabel(candidate) {
48
+ const provenance = formatCredentialProvenanceLabel(candidate);
49
+ const provenanceSuffix = provenance ? `; ${provenance}` : "";
50
+ return `${candidate.provider} (${candidate.model}${provenanceSuffix})`;
51
+ }
29
52
  function formatFailingProviderLine(provider, classification, agentName) {
30
53
  const authCommand = `ouro auth --agent ${agentName} --provider ${provider}`;
31
54
  switch (classification) {
@@ -43,27 +66,64 @@ function formatFailingProviderLine(provider, classification, agentName) {
43
66
  return ` - ${provider}: could not be reached. Run \`${authCommand}\` if credentials may be stale.`;
44
67
  }
45
68
  }
46
- function buildFailoverContext(errorMessage, classification, currentProvider, currentModel, agentName, inventory, providerModels) {
47
- const label = CLASSIFICATION_LABELS[classification];
48
- const providerWithModel = formatProviderWithModel(currentProvider, currentModel);
49
- const errorSummary = `${providerWithModel} ${label}`;
50
- const errorDetail = formatErrorDetail(errorMessage, errorSummary);
51
- const modelMismatch = (0, provider_models_1.getProviderModelMismatchMessage)(currentProvider, currentModel);
52
- const workingProviders = [];
53
- const unconfiguredProviders = [];
54
- const failingProviders = [];
69
+ function isProviderFailoverInventory(inventory) {
70
+ const candidate = inventory;
71
+ return Array.isArray(candidate.ready) && Array.isArray(candidate.unavailable) && Array.isArray(candidate.unconfigured);
72
+ }
73
+ function normalizeLegacyInventory(inventory, providerModels) {
74
+ const ready = [];
75
+ const unavailable = [];
76
+ const unconfigured = [];
55
77
  for (const [provider, result] of Object.entries(inventory)) {
56
78
  if (result.ok) {
57
- workingProviders.push(provider);
79
+ ready.push({
80
+ provider,
81
+ model: (0, provider_models_1.resolveModelForProviderDisplay)(provider, providerModels[provider]),
82
+ result,
83
+ });
58
84
  }
59
85
  else if (result.classification === "auth-failure" && result.message === "no credentials configured") {
60
- unconfiguredProviders.push(provider);
86
+ unconfigured.push(provider);
61
87
  }
62
88
  else {
63
89
  // Configured but ping failed (expired token, provider also down, etc.)
64
- failingProviders.push({ provider, classification: result.classification });
90
+ unavailable.push({
91
+ provider,
92
+ model: (0, provider_models_1.resolveModelForProviderDisplay)(provider, providerModels[provider]),
93
+ result,
94
+ });
65
95
  }
66
96
  }
97
+ return { ready, unavailable, unconfigured };
98
+ }
99
+ function normalizeFailoverInventory(inventory, providerModels) {
100
+ if (!isProviderFailoverInventory(inventory)) {
101
+ return normalizeLegacyInventory(inventory, providerModels);
102
+ }
103
+ return {
104
+ ready: inventory.ready.map((candidate) => ({
105
+ ...candidate,
106
+ model: (0, provider_models_1.resolveModelForProviderDisplay)(candidate.provider, candidate.model),
107
+ })),
108
+ unavailable: inventory.unavailable.map((candidate) => ({
109
+ ...candidate,
110
+ model: candidate.model ? (0, provider_models_1.resolveModelForProviderDisplay)(candidate.provider, candidate.model) : undefined,
111
+ })),
112
+ unconfigured: [...inventory.unconfigured],
113
+ };
114
+ }
115
+ function buildFailoverContext(errorMessage, classification, currentProvider, currentModel, agentName, inventory, providerModels, options = {}) {
116
+ const currentLane = options.currentLane ?? "outward";
117
+ const label = CLASSIFICATION_LABELS[classification];
118
+ const providerWithModel = formatProviderWithModel(currentProvider, currentModel);
119
+ const errorSummary = `${providerWithModel} ${label}`;
120
+ const errorDetail = formatErrorDetail(errorMessage, errorSummary);
121
+ const modelMismatch = (0, provider_models_1.getProviderModelMismatchMessage)(currentProvider, currentModel);
122
+ const normalizedInventory = normalizeFailoverInventory(inventory, providerModels);
123
+ const readyProviders = normalizedInventory.ready;
124
+ const workingProviders = readyProviders.map((candidate) => candidate.provider);
125
+ const unconfiguredProviders = normalizedInventory.unconfigured;
126
+ const failingProviders = normalizedInventory.unavailable;
67
127
  const lines = [`${errorSummary}.`];
68
128
  if (errorDetail) {
69
129
  lines.push(`provider detail: ${errorDetail}`);
@@ -79,22 +139,20 @@ function buildFailoverContext(errorMessage, classification, currentProvider, cur
79
139
  lines.push("Config warning:");
80
140
  lines.push(` - ${modelMismatch}`);
81
141
  lines.push(" - Repair the configured model with:");
82
- lines.push(` \`ouro config model --agent ${agentName} --facing human ${defaultModel}\``);
83
- lines.push(` \`ouro config model --agent ${agentName} --facing agent ${defaultModel}\``);
142
+ lines.push(` \`ouro use --agent ${agentName} --lane ${currentLane} --provider ${currentProvider} --model ${defaultModel}\``);
84
143
  }
85
- if (workingProviders.length > 0) {
144
+ if (readyProviders.length > 0) {
86
145
  lines.push("");
87
146
  lines.push("Ready providers:");
88
- for (const provider of workingProviders) {
89
- const model = (0, provider_models_1.resolveModelForProviderDisplay)(provider, providerModels[provider]);
90
- lines.push(` - ${provider} (${model}): reply "switch to ${provider}"`);
147
+ for (const candidate of readyProviders) {
148
+ lines.push(` - ${formatReadyProviderLabel(candidate)}: reply "switch to ${candidate.provider}"`);
91
149
  }
92
150
  }
93
151
  if (failingProviders.length > 0) {
94
152
  lines.push("");
95
153
  lines.push("Configured but unavailable:");
96
- for (const { provider, classification } of failingProviders) {
97
- lines.push(formatFailingProviderLine(provider, classification, agentName));
154
+ for (const candidate of failingProviders) {
155
+ lines.push(formatFailingProviderLine(candidate.provider, candidate.result.classification, agentName));
98
156
  }
99
157
  }
100
158
  if (unconfiguredProviders.length > 0) {
@@ -112,24 +170,105 @@ function buildFailoverContext(errorMessage, classification, currentProvider, cur
112
170
  component: "engine",
113
171
  event: "engine.failover_context_built",
114
172
  message: "built provider failover context",
115
- meta: { currentProvider, classification, workingProviders, unconfiguredProviders },
173
+ meta: { currentProvider, currentLane, classification, workingProviders, unconfiguredProviders },
116
174
  });
117
175
  return {
118
176
  errorSummary,
119
177
  classification,
120
178
  currentProvider,
179
+ currentLane,
121
180
  agentName,
122
181
  workingProviders,
182
+ readyProviders,
123
183
  unconfiguredProviders,
124
184
  userMessage: lines.join("\n"),
125
185
  };
126
186
  }
127
187
  function handleFailoverReply(reply, context) {
128
188
  const lower = reply.toLowerCase().trim();
129
- for (const provider of context.workingProviders) {
130
- if (lower.includes(`switch to ${provider}`) || lower === provider) {
131
- return { action: "switch", provider };
189
+ const readyProviders = context.readyProviders ?? context.workingProviders.map((provider) => ({
190
+ provider,
191
+ model: (0, provider_models_1.resolveModelForProviderDisplay)(provider),
192
+ }));
193
+ const currentLane = context.currentLane ?? "outward";
194
+ for (const candidate of readyProviders) {
195
+ if (lower.includes(`switch to ${candidate.provider}`) || lower === candidate.provider) {
196
+ return {
197
+ action: "switch",
198
+ provider: candidate.provider,
199
+ model: candidate.model,
200
+ lane: currentLane,
201
+ ...(candidate.credentialRevision ? { credentialRevision: candidate.credentialRevision } : {}),
202
+ ...(candidate.source ? { source: candidate.source } : {}),
203
+ ...(candidate.contributedByAgent ? { contributedByAgent: candidate.contributedByAgent } : {}),
204
+ };
132
205
  }
133
206
  }
134
207
  return { action: "dismiss" };
135
208
  }
209
+ function candidateFromCredentialRecord(record) {
210
+ return {
211
+ provider: record.provider,
212
+ credentialRevision: record.revision,
213
+ source: record.provenance.source,
214
+ contributedByAgent: record.provenance.contributedByAgent,
215
+ };
216
+ }
217
+ async function runMachineProviderFailoverInventory(agentName, currentProvider, options = {}) {
218
+ const ping = options.ping ?? provider_ping_1.pingProvider;
219
+ const poolResult = (0, provider_credential_pool_1.readProviderCredentialPool)(options.homeDir);
220
+ const providers = Object.keys(identity_1.PROVIDER_CREDENTIALS).filter((provider) => provider !== currentProvider);
221
+ const inventory = { ready: [], unavailable: [], unconfigured: [] };
222
+ if (!poolResult.ok) {
223
+ inventory.unconfigured.push(...providers);
224
+ (0, runtime_1.emitNervesEvent)({
225
+ component: "engine",
226
+ event: "engine.machine_failover_inventory_built",
227
+ message: "built machine provider failover inventory",
228
+ meta: { agentName, currentProvider, credentialPoolStatus: poolResult.reason, readyCount: 0, unavailableCount: 0, unconfiguredCount: inventory.unconfigured.length },
229
+ });
230
+ return inventory;
231
+ }
232
+ const results = await Promise.all(providers.map(async (provider) => {
233
+ const record = poolResult.pool.providers[provider];
234
+ if (!record)
235
+ return { provider, record: undefined, result: undefined };
236
+ const model = (0, provider_models_1.getDefaultModelForProvider)(provider);
237
+ const config = { ...record.credentials, ...record.config };
238
+ const result = await ping(provider, config, { model });
239
+ return { provider, record, model, result };
240
+ }));
241
+ for (const entry of results) {
242
+ if (!entry.record) {
243
+ inventory.unconfigured.push(entry.provider);
244
+ }
245
+ else if (entry.result.ok) {
246
+ inventory.ready.push({
247
+ ...candidateFromCredentialRecord(entry.record),
248
+ model: entry.model,
249
+ result: entry.result,
250
+ });
251
+ }
252
+ else {
253
+ inventory.unavailable.push({
254
+ ...candidateFromCredentialRecord(entry.record),
255
+ model: entry.model,
256
+ result: entry.result,
257
+ });
258
+ }
259
+ }
260
+ (0, runtime_1.emitNervesEvent)({
261
+ component: "engine",
262
+ event: "engine.machine_failover_inventory_built",
263
+ message: "built machine provider failover inventory",
264
+ meta: {
265
+ agentName,
266
+ currentProvider,
267
+ credentialPoolStatus: "present",
268
+ readyCount: inventory.ready.length,
269
+ unavailableCount: inventory.unavailable.length,
270
+ unconfiguredCount: inventory.unconfigured.length,
271
+ },
272
+ });
273
+ return inventory;
274
+ }
@@ -609,6 +609,8 @@ function toolContractsSection(channel, options) {
609
609
  lines.push(`- When I am ready to respond to the user, I call \`settle\`.`);
610
610
  lines.push(`- \`settle\` must be the only tool call in that turn.`);
611
611
  lines.push(`- I do not call no-op tools before \`settle\`.`);
612
+ lines.push(`- when told to work autonomously, I use ponder to absorb new messages and continue using tools. I settle only with the final result.`);
613
+ lines.push(`- if nothing calls for words, I observe.`);
612
614
  }
613
615
  }
614
616
  return lines.join("\n");
@@ -72,7 +72,7 @@ exports.settleTool = {
72
72
  type: "function",
73
73
  function: {
74
74
  name: "settle",
75
- description: "respond to the user with your message. call this tool when you are ready to deliver your response. Only call when you have a substantive response. If you're settling with 'I'll look into that,' you probably should be using a tool instead.",
75
+ description: "deliver your response and end your turn this hands control back to the user. only settle when your work is complete, you're genuinely blocked, or the user asked a direct question that needs an answer now. do not settle with status updates mid-task. if you're settling with 'I'll look into that,' you probably should be using a tool instead.",
76
76
  parameters: {
77
77
  type: "object",
78
78
  properties: {
@@ -52,9 +52,7 @@ const active_work_1 = require("../heart/active-work");
52
52
  const delegation_1 = require("../heart/delegation");
53
53
  const obligations_1 = require("../arc/obligations");
54
54
  const provider_failover_1 = require("../heart/provider-failover");
55
- const provider_ping_1 = require("../heart/provider-ping");
56
- const auth_flow_1 = require("../heart/auth/auth-flow");
57
- const provider_models_1 = require("../heart/provider-models");
55
+ const provider_state_1 = require("../heart/provider-state");
58
56
  const tempo_1 = require("../heart/tempo");
59
57
  const temporal_view_1 = require("../heart/temporal-view");
60
58
  const start_of_turn_packet_1 = require("../heart/start-of-turn-packet");
@@ -85,6 +83,52 @@ function emitObligationTransitionEpisodes(agentRoot, preTurnObligationIds, postT
85
83
  }
86
84
  }
87
85
  }
86
+ function providerLaneForChannel(channel) {
87
+ return channel === "inner" ? "inner" : "outward";
88
+ }
89
+ function resolveCurrentFailoverBinding(agentName, lane) {
90
+ const stateResult = (0, provider_state_1.readProviderState)((0, identity_1.getAgentRoot)(agentName));
91
+ if (stateResult.ok) {
92
+ const binding = stateResult.state.lanes[lane];
93
+ return { provider: binding.provider, model: binding.model };
94
+ }
95
+ const agentConfig = (0, identity_1.loadAgentConfig)();
96
+ const fallback = lane === "inner" ? agentConfig.agentFacing : agentConfig.humanFacing;
97
+ return { provider: fallback.provider, model: fallback.model };
98
+ }
99
+ function writeFailoverProviderStateSwitch(agentName, action) {
100
+ const agentRoot = (0, identity_1.getAgentRoot)(agentName);
101
+ const stateResult = (0, provider_state_1.readProviderState)(agentRoot);
102
+ if (!stateResult.ok) {
103
+ throw new Error(`Cannot switch ${action.lane} lane for ${agentName}: ${stateResult.error}`);
104
+ }
105
+ const updatedAt = new Date().toISOString();
106
+ const lanes = { ...stateResult.state.lanes };
107
+ lanes[action.lane] = {
108
+ provider: action.provider,
109
+ model: action.model,
110
+ source: "local",
111
+ updatedAt,
112
+ };
113
+ const readiness = { ...stateResult.state.readiness };
114
+ readiness[action.lane] = {
115
+ status: "ready",
116
+ provider: action.provider,
117
+ model: action.model,
118
+ checkedAt: updatedAt,
119
+ ...(action.credentialRevision ? { credentialRevision: action.credentialRevision } : {}),
120
+ };
121
+ (0, provider_state_1.writeProviderState)(agentRoot, {
122
+ ...stateResult.state,
123
+ updatedAt,
124
+ lanes,
125
+ readiness,
126
+ });
127
+ }
128
+ function formatFailoverSwitchLabel(action) {
129
+ const provenance = (0, provider_failover_1.formatCredentialProvenanceLabel)(action);
130
+ return `${action.provider} (${action.model}${provenance ? `; ${provenance}` : ""})`;
131
+ }
88
132
  function prependTurnSections(message, sections) {
89
133
  /* v8 ignore next -- defensive: only user messages with non-empty sections reach here @preserve */
90
134
  if (message.role !== "user" || sections.length === 0)
@@ -133,8 +177,7 @@ async function handleInboundTurn(input) {
133
177
  if (failoverAction.action === "switch") {
134
178
  let switchSucceeded = false;
135
179
  try {
136
- (0, auth_flow_1.writeAgentProviderSelection)(failoverAgentName, "human", failoverAction.provider);
137
- (0, auth_flow_1.writeAgentProviderSelection)(failoverAgentName, "agent", failoverAction.provider);
180
+ writeFailoverProviderStateSwitch(failoverAgentName, failoverAction);
138
181
  switchSucceeded = true;
139
182
  /* v8 ignore start -- defensive: write failure during provider switch @preserve */
140
183
  }
@@ -143,8 +186,8 @@ async function handleInboundTurn(input) {
143
186
  level: "error",
144
187
  component: "senses",
145
188
  event: "senses.failover_switch_error",
146
- message: `failed to switch provider to ${failoverAction.provider}`,
147
- meta: { agentName: failoverAgentName, provider: failoverAction.provider, error: switchError instanceof Error ? switchError.message : String(switchError) },
189
+ message: `failed to switch ${failoverAction.lane} provider lane to ${failoverAction.provider}`,
190
+ meta: { agentName: failoverAgentName, lane: failoverAction.lane, provider: failoverAction.provider, model: failoverAction.model, error: switchError instanceof Error ? switchError.message : String(switchError) },
148
191
  });
149
192
  }
150
193
  /* v8 ignore stop */
@@ -153,28 +196,24 @@ async function handleInboundTurn(input) {
153
196
  (0, runtime_1.emitNervesEvent)({
154
197
  component: "senses",
155
198
  event: "senses.failover_switch",
156
- message: `switched provider to ${failoverAction.provider} via failover`,
157
- meta: { agentName: failoverAgentName, provider: failoverAction.provider },
199
+ message: `switched ${failoverAction.lane} provider lane to ${failoverAction.provider} via failover`,
200
+ meta: {
201
+ agentName: failoverAgentName,
202
+ lane: failoverAction.lane,
203
+ provider: failoverAction.provider,
204
+ model: failoverAction.model,
205
+ credentialRevision: failoverAction.credentialRevision,
206
+ source: failoverAction.source,
207
+ contributedByAgent: failoverAction.contributedByAgent,
208
+ },
158
209
  });
159
210
  // Replace "switch to <provider>" with a context message for the agent.
160
211
  // The session already has the user's original question from the failed turn.
161
212
  // The agent needs to know what happened so it can respond appropriately.
162
- const newProviderSecrets = (() => {
163
- try {
164
- const { secrets } = (0, auth_flow_1.loadAgentSecrets)(failoverAgentName);
165
- const cfg = secrets.providers[failoverAction.provider];
166
- const hint = cfg?.model ?? cfg?.modelName;
167
- return (0, provider_models_1.resolveModelForProviderDisplay)(failoverAction.provider, typeof hint === "string" ? hint : "");
168
- /* v8 ignore next 2 -- defensive: secrets read failure @preserve */
169
- }
170
- catch {
171
- return (0, provider_models_1.resolveModelForProviderDisplay)(failoverAction.provider);
172
- }
173
- })();
174
- const newProviderLabel = `${failoverAction.provider} (${newProviderSecrets})`;
213
+ const newProviderLabel = formatFailoverSwitchLabel(failoverAction);
175
214
  input.messages = [{
176
215
  role: "user",
177
- content: `[provider switch: ${pendingContext.errorSummary}. switched to ${newProviderLabel}. your conversation history is intact — respond to the user's last message.]`,
216
+ content: `[provider switch: ${pendingContext.errorSummary}. switched ${failoverAction.lane} lane to ${newProviderLabel}. your conversation history is intact — respond to the user's last message.]`,
178
217
  }];
179
218
  input.switchedProvider = failoverAction.provider;
180
219
  }
@@ -465,23 +504,15 @@ async function handleInboundTurn(input) {
465
504
  if (result.outcome === "errored" && input.failoverState) {
466
505
  try {
467
506
  const agentName = (0, identity_1.getAgentName)();
468
- const agentConfig = (0, identity_1.loadAgentConfig)();
469
- const currentProvider = agentConfig.humanFacing.provider;
507
+ const currentLane = providerLaneForChannel(input.channel);
508
+ const currentBinding = resolveCurrentFailoverBinding(agentName, currentLane);
509
+ const currentProvider = currentBinding.provider;
470
510
  /* v8 ignore next -- defensive: errorClassification always set when errored @preserve */
471
511
  const classification = result.errorClassification ?? "unknown";
472
- const inventory = await (0, provider_ping_1.runHealthInventory)(agentName, currentProvider);
473
- const { secrets } = (0, auth_flow_1.loadAgentSecrets)(agentName);
474
- const providerModels = {};
475
- for (const [p, cfg] of Object.entries(secrets.providers)) {
476
- const model = cfg.model ?? cfg.modelName;
477
- if (typeof model === "string" && model)
478
- providerModels[p] = model;
479
- }
480
- // Use agent.json model (source of truth), not secrets model (may be stale)
481
- const currentModel = agentConfig.humanFacing.model;
512
+ const inventory = await (0, provider_failover_1.runMachineProviderFailoverInventory)(agentName, currentProvider);
482
513
  const failoverContext = (0, provider_failover_1.buildFailoverContext)(
483
514
  /* v8 ignore next -- defensive: error always set when errored @preserve */
484
- result.error?.message ?? "unknown error", classification, currentProvider, currentModel, agentName, inventory, providerModels);
515
+ result.error?.message ?? "unknown error", classification, currentProvider, currentBinding.model, agentName, inventory, {}, { currentLane });
485
516
  input.failoverState.pending = failoverContext;
486
517
  input.postTurn(sessionMessages, session.sessionPath, result.usage);
487
518
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.351",
3
+ "version": "0.1.0-alpha.353",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",