@ouro.bot/cli 0.1.0-alpha.552 → 0.1.0-alpha.554

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.
@@ -40,8 +40,6 @@ var __importStar = (this && this.__importStar) || (function () {
40
40
  })();
41
41
  Object.defineProperty(exports, "__esModule", { value: true });
42
42
  exports.summarizeDaemonStartupFailure = summarizeDaemonStartupFailure;
43
- exports.writeDriftAdvisorySummary = writeDriftAdvisorySummary;
44
- exports.collectAgentDriftAdvisories = collectAgentDriftAdvisories;
45
43
  exports.writeSyncProbeSummary = writeSyncProbeSummary;
46
44
  exports.summarizeSyncProbeFindings = summarizeSyncProbeFindings;
47
45
  exports.mergeStartupStability = mergeStartupStability;
@@ -78,7 +76,6 @@ const provider_credentials_1 = require("../provider-credentials");
78
76
  const runtime_credentials_1 = require("../runtime-credentials");
79
77
  const provider_binding_resolver_1 = require("../provider-binding-resolver");
80
78
  const provider_visibility_1 = require("../provider-visibility");
81
- const provider_state_1 = require("../provider-state");
82
79
  const machine_identity_1 = require("../machine-identity");
83
80
  const provider_models_1 = require("../provider-models");
84
81
  const ouro_version_manager_1 = require("../versioning/ouro-version-manager");
@@ -98,7 +95,6 @@ const cli_help_1 = require("./cli-help");
98
95
  const cli_render_1 = require("./cli-render");
99
96
  const cli_defaults_1 = require("./cli-defaults");
100
97
  const agent_config_check_1 = require("./agent-config-check");
101
- const drift_detection_1 = require("./drift-detection");
102
98
  const doctor_1 = require("./doctor");
103
99
  const cli_render_doctor_1 = require("./cli-render-doctor");
104
100
  const interactive_repair_1 = require("./interactive-repair");
@@ -486,61 +482,6 @@ function daemonUnavailableStatusJsonOutput(socketPath, healthFilePath) {
486
482
  ...(healthFilePath ? { healthFilePath } : {}),
487
483
  }, null, 2);
488
484
  }
489
- /**
490
- * Layer 4: render a per-agent drift advisory block to stdout. Called from
491
- * the `--no-repair` summary path when one or more enabled agents have a
492
- * mismatch between `agent.json` (intent) and `state/providers.json`
493
- * (observation). The block surfaces the lane, intent vs observed
494
- * binding, and the copy-pasteable `ouro use` repair command per finding.
495
- *
496
- * Drift is advisory: this helper only PRINTS the advisory; running the
497
- * repair command is the operator's call (and is Layer 3 RepairGuide's
498
- * domain when the daemon does it automatically).
499
- *
500
- * Exported for direct unit-testing; the production callers are
501
- * inside this module.
502
- */
503
- function writeDriftAdvisorySummary(deps, advisories) {
504
- if (advisories.length === 0)
505
- return;
506
- const lines = ["Drift advisory: agent intent does not match this machine's observed binding"];
507
- for (const advisory of advisories) {
508
- lines.push("");
509
- lines.push(` ${advisory.agent} (${advisory.lane}):`);
510
- lines.push(` intent: ${advisory.intentProvider}/${advisory.intentModel}`);
511
- lines.push(` observed: ${advisory.observedProvider}/${advisory.observedModel}`);
512
- lines.push(` repair: ${advisory.repairCommand}`);
513
- }
514
- deps.writeStdout(lines.join("\n"));
515
- }
516
- /**
517
- * Collect drift findings across every enabled agent in the bundles
518
- * directory. Used by the `--no-repair` summary path so that drift
519
- * advisories ride along with (or stand alone in place of) the existing
520
- * provider-repair summary. Errors during a single agent's load (e.g.
521
- * malformed `agent.json`) are swallowed: drift detection is advisory
522
- * and must not block the rest of the boot path.
523
- */
524
- async function collectAgentDriftAdvisories(deps) {
525
- const agents = await listCliAgents(deps);
526
- const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
527
- const findings = [];
528
- for (const agent of [...new Set(agents)]) {
529
- try {
530
- const inputs = (0, drift_detection_1.loadDriftInputsForAgent)(bundlesRoot, agent);
531
- const agentFindings = (0, drift_detection_1.detectProviderBindingDrift)({
532
- agentName: agent,
533
- agentJson: inputs.agentJson,
534
- providerState: inputs.providerState,
535
- });
536
- findings.push(...agentFindings);
537
- }
538
- catch {
539
- // Best-effort: a per-agent read failure is not blocking.
540
- }
541
- }
542
- return findings;
543
- }
544
485
  function providerRepairCountSummary(count) {
545
486
  if (count === 0)
546
487
  return "selected providers answered live checks";
@@ -1305,7 +1246,7 @@ async function resolveHatchInput(command, deps) {
1305
1246
  migrationPath: command.migrationPath,
1306
1247
  };
1307
1248
  }
1308
- // ── Provider state CLI helpers ──
1249
+ // ── Provider selection CLI helpers ──
1309
1250
  function providerCliHomeDir(deps) {
1310
1251
  return (0, provider_credentials_1.providerCredentialMachineHomeDir)(deps.homeDir);
1311
1252
  }
@@ -4343,56 +4284,12 @@ async function executeConnect(command, deps) {
4343
4284
  deps.writeStdout(message);
4344
4285
  return message;
4345
4286
  }
4346
- function readOrBootstrapProviderState(agentName, deps) {
4347
- const agentRoot = providerCliAgentRoot({ agent: agentName }, deps);
4348
- const readResult = (0, provider_state_1.readProviderState)(agentRoot);
4349
- if (readResult.ok)
4350
- return { agentRoot, state: readResult.state };
4351
- if (readResult.reason === "invalid") {
4352
- throw new Error(`provider state for ${agentName} is invalid at ${readResult.statePath}: ${readResult.error}`);
4353
- }
4354
- const { config } = (0, auth_flow_1.readAgentConfigForAgent)(agentName, deps.bundlesRoot);
4355
- const homeDir = providerCliHomeDir(deps);
4356
- const machine = (0, machine_identity_1.loadOrCreateMachineIdentity)({
4357
- homeDir,
4358
- now: () => providerCliNow(deps),
4359
- });
4360
- const state = (0, provider_state_1.bootstrapProviderStateFromAgentConfig)({
4361
- machineId: machine.machineId,
4362
- now: providerCliNow(deps),
4363
- agentConfig: {
4364
- humanFacing: {
4365
- provider: config.humanFacing.provider,
4366
- model: config.humanFacing.model || (0, provider_models_1.getDefaultModelForProvider)(config.humanFacing.provider),
4367
- },
4368
- agentFacing: {
4369
- provider: config.agentFacing.provider,
4370
- model: config.agentFacing.model || (0, provider_models_1.getDefaultModelForProvider)(config.agentFacing.provider),
4371
- },
4372
- },
4373
- });
4374
- (0, provider_state_1.writeProviderState)(agentRoot, state);
4375
- (0, runtime_1.emitNervesEvent)({
4376
- component: "daemon",
4377
- event: "daemon.provider_state_bootstrapped",
4378
- message: "bootstrapped local provider state from agent config",
4379
- meta: { agent: agentName, agentRoot },
4380
- });
4381
- return { agentRoot, state };
4382
- }
4383
4287
  function credentialPingConfig(record) {
4384
4288
  return {
4385
4289
  ...record.credentials,
4386
4290
  ...record.config,
4387
4291
  };
4388
4292
  }
4389
- function pingAttemptCount(result) {
4390
- if (typeof result.attempts === "number")
4391
- return result.attempts;
4392
- if (Array.isArray(result.attempts))
4393
- return result.attempts.length;
4394
- return undefined;
4395
- }
4396
4293
  async function readProviderCredentialRecord(agent, provider, _deps, options = {}) {
4397
4294
  const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(agent, { ...options, providers: [provider] });
4398
4295
  if (poolResult.ok) {
@@ -4410,39 +4307,38 @@ async function readProviderCredentialRecord(agent, provider, _deps, options = {}
4410
4307
  error: `no credentials stored for ${provider}`,
4411
4308
  };
4412
4309
  }
4413
- function writeProviderBinding(input) {
4414
- const updatedAt = providerCliNow(input.deps).toISOString();
4415
- input.state.updatedAt = updatedAt;
4416
- input.state.lanes[input.lane] = {
4417
- provider: input.provider,
4418
- model: input.model,
4419
- source: "local",
4420
- updatedAt,
4421
- };
4422
- input.state.readiness[input.lane] = {
4423
- status: input.status,
4424
- provider: input.provider,
4425
- model: input.model,
4426
- checkedAt: updatedAt,
4427
- ...(input.credentialRevision ? { credentialRevision: input.credentialRevision } : {}),
4428
- ...(input.error ? { error: input.error } : {}),
4429
- ...(input.attempts !== undefined ? { attempts: input.attempts } : {}),
4310
+ function providerFacingKeyForLane(lane) {
4311
+ return lane === "inner" ? "agentFacing" : "humanFacing";
4312
+ }
4313
+ function providerConfigBinding(config, lane) {
4314
+ const binding = config[providerFacingKeyForLane(lane)];
4315
+ return {
4316
+ provider: binding.provider,
4317
+ model: binding.model || (0, provider_models_1.getDefaultModelForProvider)(binding.provider),
4430
4318
  };
4431
- (0, provider_state_1.writeProviderState)(input.agentRoot, input.state);
4432
- }
4433
- function writeProviderReadiness(input) {
4434
- const checkedAt = providerCliNow(input.deps).toISOString();
4435
- input.state.updatedAt = checkedAt;
4436
- input.state.readiness[input.lane] = {
4437
- status: input.status,
4438
- provider: input.provider,
4439
- model: input.model,
4440
- checkedAt,
4441
- credentialRevision: input.credentialRevision,
4442
- ...(input.error ? { error: input.error } : {}),
4443
- ...(input.attempts !== undefined ? { attempts: input.attempts } : {}),
4319
+ }
4320
+ function writeAgentProviderBinding(input) {
4321
+ const { configPath, config } = (0, auth_flow_1.readAgentConfigForAgent)(input.agent, input.deps.bundlesRoot);
4322
+ const facingKey = providerFacingKeyForLane(input.lane);
4323
+ const nextConfig = {
4324
+ ...config,
4325
+ [facingKey]: {
4326
+ ...config[facingKey],
4327
+ provider: input.provider,
4328
+ model: input.model,
4329
+ },
4444
4330
  };
4445
- (0, provider_state_1.writeProviderState)(input.agentRoot, input.state);
4331
+ fs.writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf-8");
4332
+ (0, runtime_1.emitNervesEvent)({
4333
+ component: "daemon",
4334
+ event: "daemon.provider_selection_updated",
4335
+ message: "updated provider selection in agent config",
4336
+ meta: { agent: input.agent, lane: input.lane, provider: input.provider, model: input.model, configPath },
4337
+ });
4338
+ }
4339
+ function appendBundleSyncSummary(message, agent, deps) {
4340
+ const syncSummary = pushAgentBundleAfterCliMutation(agent, deps);
4341
+ return syncSummary ? [message, syncSummary].join("\n") : message;
4446
4342
  }
4447
4343
  async function executeProviderUse(command, deps, options = {}) {
4448
4344
  const progress = options.writeStdout === false ? null : createHumanCommandProgress(deps, "provider use");
@@ -4453,7 +4349,6 @@ async function executeProviderUse(command, deps, options = {}) {
4453
4349
  return message;
4454
4350
  };
4455
4351
  try {
4456
- const { agentRoot, state } = readOrBootstrapProviderState(command.agent, deps);
4457
4352
  progress?.startPhase(`reading ${command.provider} credentials`);
4458
4353
  const credential = await readProviderCredentialRecord(command.agent, command.provider, deps, {
4459
4354
  onProgress: (message) => progress?.updateDetail(message),
@@ -4467,17 +4362,14 @@ async function executeProviderUse(command, deps, options = {}) {
4467
4362
  ].join("\n");
4468
4363
  return writeMessage(message);
4469
4364
  }
4470
- writeProviderBinding({
4471
- agentRoot,
4472
- state,
4365
+ writeAgentProviderBinding({
4366
+ agent: command.agent,
4473
4367
  lane: command.lane,
4474
4368
  provider: command.provider,
4475
4369
  model: command.model,
4476
4370
  deps,
4477
- status: "failed",
4478
- error: credential.error,
4479
4371
  });
4480
- const message = `forced ${command.agent} ${command.lane} to ${command.provider} / ${command.model}: failed (${credential.error})`;
4372
+ const message = appendBundleSyncSummary(`forced ${command.agent} ${command.lane} to ${command.provider} / ${command.model}: failed (${credential.error})`, command.agent, deps);
4481
4373
  return writeMessage(message);
4482
4374
  }
4483
4375
  progress?.completePhase(`reading ${command.provider} credentials`, "found");
@@ -4488,29 +4380,23 @@ async function executeProviderUse(command, deps, options = {}) {
4488
4380
  sleep: deps.sleep,
4489
4381
  ...(0, provider_ping_progress_1.createProviderPingProgressReporter)({ provider: command.provider, model: command.model }, (message) => progress?.updateDetail(message)),
4490
4382
  });
4491
- const attempts = pingAttemptCount(pingResult);
4492
4383
  const status = pingResult.ok ? "ready" : `failed (${pingResult.message})`;
4493
4384
  progress?.completePhase(`checking ${command.provider} / ${command.model}`, status);
4494
4385
  if (!pingResult.ok && !command.force) {
4495
4386
  const message = [
4496
4387
  `${command.agent} ${command.lane} ${command.provider} / ${command.model}: failed (${pingResult.message})`,
4497
- `Fix credentials with \`ouro auth --agent ${command.agent} --provider ${command.provider}\` or force the local binding with \`ouro use --agent ${command.agent} --lane ${command.lane} --provider ${command.provider} --model ${command.model} --force\`.`,
4388
+ `Fix credentials with \`ouro auth --agent ${command.agent} --provider ${command.provider}\` or force agent.json with \`ouro use --agent ${command.agent} --lane ${command.lane} --provider ${command.provider} --model ${command.model} --force\`.`,
4498
4389
  ].join("\n");
4499
4390
  return writeMessage(message);
4500
4391
  }
4501
- writeProviderBinding({
4502
- agentRoot,
4503
- state,
4392
+ writeAgentProviderBinding({
4393
+ agent: command.agent,
4504
4394
  lane: command.lane,
4505
4395
  provider: command.provider,
4506
4396
  model: command.model,
4507
4397
  deps,
4508
- status: pingResult.ok ? "ready" : "failed",
4509
- credentialRevision: credential.record.revision,
4510
- ...(!pingResult.ok ? { error: pingResult.message } : {}),
4511
- ...(attempts !== undefined ? { attempts } : {}),
4512
4398
  });
4513
- const message = `${command.force ? "forced " : ""}${command.agent} ${command.lane} ${command.provider} / ${command.model}: ${status}`;
4399
+ const message = appendBundleSyncSummary(`${command.force ? "forced " : ""}${command.agent} ${command.lane} ${command.provider} / ${command.model}: ${status}`, command.agent, deps);
4514
4400
  (0, runtime_1.emitNervesEvent)({
4515
4401
  component: "daemon",
4516
4402
  event: "daemon.provider_use_completed",
@@ -4532,8 +4418,8 @@ async function executeProviderCheck(command, deps) {
4532
4418
  return message;
4533
4419
  };
4534
4420
  try {
4535
- const { agentRoot, state } = readOrBootstrapProviderState(command.agent, deps);
4536
- const binding = state.lanes[command.lane];
4421
+ const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot);
4422
+ const binding = providerConfigBinding(config, command.lane);
4537
4423
  progress.startPhase(`reading ${binding.provider} credentials`);
4538
4424
  const credential = await readProviderCredentialRecord(command.agent, binding.provider, deps, {
4539
4425
  onProgress: (message) => progress.updateDetail(message),
@@ -4554,21 +4440,8 @@ async function executeProviderCheck(command, deps) {
4554
4440
  sleep: deps.sleep,
4555
4441
  ...(0, provider_ping_progress_1.createProviderPingProgressReporter)({ provider: binding.provider, model: binding.model }, (message) => progress.updateDetail(message)),
4556
4442
  });
4557
- const attempts = pingAttemptCount(pingResult);
4558
4443
  const status = pingResult.ok ? "ready" : `failed (${pingResult.message})`;
4559
4444
  progress.completePhase(`checking ${binding.provider} / ${binding.model}`, status);
4560
- writeProviderReadiness({
4561
- agentRoot,
4562
- state,
4563
- lane: command.lane,
4564
- provider: binding.provider,
4565
- model: binding.model,
4566
- deps,
4567
- status: pingResult.ok ? "ready" : "failed",
4568
- credentialRevision: credential.record.revision,
4569
- ...(!pingResult.ok ? { error: pingResult.message } : {}),
4570
- ...(attempts !== undefined ? { attempts } : {}),
4571
- });
4572
4445
  const message = `${command.agent} ${command.lane} ${binding.provider} / ${binding.model}: ${status}`;
4573
4446
  (0, runtime_1.emitNervesEvent)({
4574
4447
  component: "daemon",
@@ -4577,11 +4450,13 @@ async function executeProviderCheck(command, deps) {
4577
4450
  meta: { agent: command.agent, lane: command.lane, provider: binding.provider, model: binding.model, status: pingResult.ok ? "ready" : "failed" },
4578
4451
  });
4579
4452
  return writeMessage(message);
4453
+ /* v8 ignore start -- defensive rethrow: provider-check dependency failures are covered at the caller boundary @preserve */
4580
4454
  }
4581
4455
  catch (error) {
4582
4456
  progress.end();
4583
4457
  throw error;
4584
4458
  }
4459
+ /* v8 ignore stop */
4585
4460
  }
4586
4461
  function renderProviderCredentialLine(agentName, credential) {
4587
4462
  if (credential.status === "present") {
@@ -4600,12 +4475,12 @@ function renderProviderCredentialLine(agentName, credential) {
4600
4475
  async function executeProviderStatus(command, deps) {
4601
4476
  const agentRoot = providerCliAgentRoot(command, deps);
4602
4477
  const progress = createHumanCommandProgress(deps, "provider status");
4603
- const stateResult = (0, provider_state_1.readProviderState)(agentRoot);
4604
4478
  try {
4605
- if (stateResult.ok) {
4479
+ const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot);
4480
+ {
4606
4481
  const selectedProviders = [...new Set([
4607
- stateResult.state.lanes.outward.provider,
4608
- stateResult.state.lanes.inner.provider,
4482
+ config.humanFacing.provider,
4483
+ config.agentFacing.provider,
4609
4484
  ])];
4610
4485
  await runCommandProgressPhase(progress, "reading selected provider credentials", () => (0, provider_credentials_1.refreshProviderCredentialPool)(command.agent, {
4611
4486
  providers: selectedProviders,
@@ -4630,14 +4505,17 @@ async function executeProviderStatus(command, deps) {
4630
4505
  homeDir,
4631
4506
  lane,
4632
4507
  });
4508
+ /* v8 ignore start -- defensive: provider status pre-validates agent.json before resolving lane details @preserve */
4633
4509
  if (!resolved.ok) {
4634
4510
  lines.push(` ${lane}: unavailable`);
4635
- lines.push(` ${resolved.reason}: ${resolved.repair.command}`);
4511
+ lines.push(` ${resolved.reason}: ${resolved.error}`);
4512
+ lines.push(` repair: ${resolved.repair.command}`);
4636
4513
  continue;
4637
4514
  }
4515
+ /* v8 ignore stop */
4638
4516
  const binding = resolved.binding;
4639
4517
  lines.push(` ${lane}: ${binding.provider} / ${binding.model} (${binding.source})`);
4640
- lines.push(` readiness: ${binding.readiness.status}${binding.readiness.error ? ` (${binding.readiness.error})` : ""}`);
4518
+ lines.push(` readiness: ${binding.readiness.status}`);
4641
4519
  lines.push(` ${renderProviderCredentialLine(command.agent, binding.credential)}`);
4642
4520
  for (const warning of binding.warnings) {
4643
4521
  lines.push(` warning: ${warning.message}`);
@@ -4668,7 +4546,7 @@ async function executeProviderRefresh(command, deps) {
4668
4546
  const lines = [];
4669
4547
  if (pool.ok) {
4670
4548
  const summary = (0, provider_credentials_1.summarizeProviderCredentialPool)(pool.pool);
4671
- lines.push(`refreshed provider credential snapshot for ${command.agent}`);
4549
+ lines.push(`refreshed in-memory provider credentials for ${command.agent}`);
4672
4550
  lines.push(`providers: ${summary.providers.map((provider) => provider.provider).join(", ") || "none"}`);
4673
4551
  progress.completePhase("refreshing provider credentials", summary.providers.map((provider) => provider.provider).join(", ") || "none");
4674
4552
  }
@@ -4910,13 +4788,13 @@ async function runReadinessRepairForDegraded(degraded, deps) {
4910
4788
  return { repairsAttempted, remainingDegraded: current };
4911
4789
  }
4912
4790
  async function executeLegacyAuthSwitch(command, deps) {
4913
- const { state } = readOrBootstrapProviderState(command.agent, deps);
4791
+ const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot);
4914
4792
  const lanes = command.facing
4915
4793
  ? [command.facing === "human" ? "outward" : "inner"]
4916
4794
  : ["outward", "inner"];
4917
4795
  const messages = [];
4918
4796
  for (const lane of lanes) {
4919
- const model = state.lanes[lane].model;
4797
+ const model = providerConfigBinding(config, lane).model;
4920
4798
  messages.push(await executeProviderUse({
4921
4799
  kind: "provider.use",
4922
4800
  agent: command.agent,
@@ -4928,7 +4806,7 @@ async function executeLegacyAuthSwitch(command, deps) {
4928
4806
  }, deps, { writeStdout: false }));
4929
4807
  }
4930
4808
  const message = [
4931
- `deprecated: switched this machine's local provider binding. \`ouro auth switch\` no longer edits agent.json.`,
4809
+ `deprecated: switched provider selection in agent.json.`,
4932
4810
  ...messages,
4933
4811
  `Use \`ouro use --agent ${command.agent} --lane <outward|inner> --provider ${command.provider} --model <model>\` for explicit provider/model selection.`,
4934
4812
  ].join("\n");
@@ -4937,8 +4815,8 @@ async function executeLegacyAuthSwitch(command, deps) {
4937
4815
  }
4938
4816
  async function executeLegacyConfigModel(command, deps) {
4939
4817
  const lane = command.facing === "agent" ? "inner" : "outward";
4940
- const { agentRoot, state } = readOrBootstrapProviderState(command.agent, deps);
4941
- const binding = state.lanes[lane];
4818
+ const { configPath, config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot);
4819
+ const binding = providerConfigBinding(config, lane);
4942
4820
  const progress = binding.provider === "github-copilot" ? createHumanCommandProgress(deps, "config model") : null;
4943
4821
  const writeMessage = (message) => {
4944
4822
  progress?.end();
@@ -4996,17 +4874,16 @@ async function executeLegacyConfigModel(command, deps) {
4996
4874
  throw error;
4997
4875
  }
4998
4876
  }
4999
- const updatedAt = providerCliNow(deps).toISOString();
5000
- state.updatedAt = updatedAt;
5001
- state.lanes[lane] = {
5002
- ...binding,
5003
- model: command.modelName,
5004
- source: "local",
5005
- updatedAt,
4877
+ const facingKey = providerFacingKeyForLane(lane);
4878
+ const nextConfig = {
4879
+ ...config,
4880
+ [facingKey]: {
4881
+ ...config[facingKey],
4882
+ model: command.modelName,
4883
+ },
5006
4884
  };
5007
- delete state.readiness[lane];
5008
- (0, provider_state_1.writeProviderState)(agentRoot, state);
5009
- const message = `deprecated: updated ${command.agent} model on ${lane}/${binding.provider}: ${binding.model} -> ${command.modelName}\nUse \`ouro use --agent ${command.agent} --lane ${lane} --provider ${binding.provider} --model ${command.modelName}\` next time.`;
4885
+ fs.writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf-8");
4886
+ const message = appendBundleSyncSummary(`deprecated: updated ${command.agent} model in agent.json on ${lane}/${binding.provider}: ${binding.model} -> ${command.modelName}\nUse \`ouro use --agent ${command.agent} --lane ${lane} --provider ${binding.provider} --model ${command.modelName}\` next time.`, command.agent, deps);
5010
4887
  return writeMessage(message);
5011
4888
  }
5012
4889
  // ── System setup ──
@@ -5942,10 +5819,6 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
5942
5819
  if (command.noRepair) {
5943
5820
  // --no-repair: write degraded summary and skip interactive repair
5944
5821
  writeProviderRepairSummary(deps, "Provider checks need attention", daemonResult.stability.degraded);
5945
- // Layer 4: drift advisories ride along with the post-startup
5946
- // degraded summary too — same rationale as the preflight path.
5947
- const driftAdvisories = await collectAgentDriftAdvisories(deps);
5948
- writeDriftAdvisorySummary(deps, driftAdvisories);
5949
5822
  deps.setExitCode?.(1);
5950
5823
  (0, runtime_1.emitNervesEvent)({
5951
5824
  level: "warn",
@@ -5954,7 +5827,6 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
5954
5827
  message: "degraded agents detected with --no-repair, skipping interactive repair",
5955
5828
  meta: {
5956
5829
  degradedCount: daemonResult.stability.degraded.length,
5957
- driftCount: driftAdvisories.length,
5958
5830
  },
5959
5831
  });
5960
5832
  }
@@ -5988,16 +5860,6 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
5988
5860
  if (repairGuideShouldFire) {
5989
5861
  const repairInput = [...untypedDegraded, ...typedDegraded];
5990
5862
  const forceDiagnosis = untypedDegraded.length === 0 && typedDegraded.length >= 3;
5991
- // Layer 3: collect drift findings here so the RepairGuide
5992
- // prompt receives them as a structured JSON block. Drift is
5993
- // already collected for the no-repair path above; we collect
5994
- // again here because the repair path is a separate branch.
5995
- // Filter to agents in repairInput so the diagnostic prompt
5996
- // doesn't carry drift from healthy peers — narrows the
5997
- // signal to the set being diagnosed.
5998
- const repairAgentNames = new Set(repairInput.map((entry) => entry.agent));
5999
- const repairDriftFindings = (await collectAgentDriftAdvisories(deps))
6000
- .filter((finding) => repairAgentNames.has(finding.agent));
6001
5863
  const repairResult = await (0, agentic_repair_1.runAgenticRepair)(repairInput, {
6002
5864
  /* v8 ignore start -- production provider discovery wiring @preserve */
6003
5865
  discoverWorkingProvider: async (agentName) => {
@@ -6043,7 +5905,6 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
6043
5905
  isTTY: deps.isTTY ?? process.stdout.isTTY === true,
6044
5906
  stdoutColumns: deps.stdoutColumns ?? process.stdout.columns,
6045
5907
  forceDiagnosis,
6046
- driftFindings: repairDriftFindings,
6047
5908
  syncFindings: bootSyncFindings,
6048
5909
  });
6049
5910
  if (repairResult.repairsAttempted) {
@@ -6077,13 +5938,6 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
6077
5938
  }
6078
5939
  }
6079
5940
  }
6080
- else if (command.noRepair) {
6081
- // Layer 4: no degraded agents to summarize, but --no-repair still
6082
- // surfaces drift advisories so the operator sees them without
6083
- // having to run `ouro inner status` per agent.
6084
- const driftAdvisories = await collectAgentDriftAdvisories(deps);
6085
- writeDriftAdvisorySummary(deps, driftAdvisories);
6086
- }
6087
5941
  return daemonResult.message;
6088
5942
  }
6089
5943
  if (command.kind === "daemon.dev") {
@@ -6680,7 +6534,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
6680
6534
  deps.writeStdout(message);
6681
6535
  return message;
6682
6536
  }
6683
- // ── provider state commands (local, no daemon socket needed) ──
6537
+ // ── provider commands (local, no daemon socket needed) ──
6684
6538
  if (command.kind === "provider.use") {
6685
6539
  return executeProviderUse(command, deps);
6686
6540
  }
@@ -7196,22 +7050,6 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
7196
7050
  catch { /* no habits — heartbeat unknown */ }
7197
7051
  // Attention count
7198
7052
  const activeObligations = listActiveReturnObligations(command.agent);
7199
- // Layer 4 drift findings for this agent. Best-effort: if agent.json
7200
- // is unreadable mid-call we just suppress the advisory rather than
7201
- // failing the inner-status render.
7202
- let driftFindings = [];
7203
- try {
7204
- const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
7205
- const inputs = (0, drift_detection_1.loadDriftInputsForAgent)(bundlesRoot, command.agent);
7206
- driftFindings = (0, drift_detection_1.detectProviderBindingDrift)({
7207
- agentName: command.agent,
7208
- agentJson: inputs.agentJson,
7209
- providerState: inputs.providerState,
7210
- });
7211
- }
7212
- catch {
7213
- driftFindings = [];
7214
- }
7215
7053
  const message = buildInnerStatusOutput({
7216
7054
  agentName: command.agent,
7217
7055
  runtimeState,
@@ -7219,7 +7057,6 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
7219
7057
  heartbeat,
7220
7058
  attentionCount: activeObligations.length,
7221
7059
  now: Date.now(),
7222
- driftFindings,
7223
7060
  });
7224
7061
  deps.writeStdout(message);
7225
7062
  return message;
@@ -289,7 +289,7 @@ const SUBCOMMAND_HELP = {
289
289
  example: "ouro auth verify --provider openai-codex",
290
290
  },
291
291
  "auth switch": {
292
- description: "Switch local provider/model lanes after credentials are available",
292
+ description: "Switch agent.json provider/model lanes after credentials are available",
293
293
  usage: "ouro auth switch [--agent <name>] --provider <provider> [--facing human|agent]",
294
294
  example: "ouro auth switch --provider minimax",
295
295
  },
@@ -623,26 +623,14 @@ function renderRollupStatusLine(health) {
623
623
  case "healthy":
624
624
  return `Last known status: healthy ${tail}`;
625
625
  case "partial": {
626
- // `partial` arises from two independent sources:
627
- // (a) some enabled agents not in `running` state, or
628
- // (b) Layer-4 drift between agent.json (intent) and state/providers.json (observed).
629
- // Either or both can be true. Render mentions both when applicable so
630
- // the operator isn't misled into thinking agents are unhealthy when the
631
- // only anomaly is configuration drift.
632
626
  const unhealthyCount = Object.values(health.agents).filter((agent) => agent.status !== "running").length;
633
- const driftCount = (health.drift ?? []).length;
634
627
  const parts = [];
635
628
  if (unhealthyCount > 0) {
636
629
  parts.push(`${unhealthyCount} agent${unhealthyCount === 1 ? "" : "s"} unhealthy`);
637
630
  }
638
- if (driftCount > 0) {
639
- parts.push(`drift on ${driftCount} agent${driftCount === 1 ? "" : "s"}`);
640
- }
641
631
  // Fallback for legacy cached files: status="partial" with no
642
- // visible cause (no unhealthy agents AND no drift array). This
643
- // happens when the file pre-dates Layer 4 and the daemon flagged
644
- // partial via some signal that's no longer expressible. Prompt
645
- // refresh via `ouro up` rather than asserting a specific cause.
632
+ // visible cause. Prompt refresh via `ouro up` rather than asserting
633
+ // a specific cause.
646
634
  const detail = parts.length > 0
647
635
  ? ` — ${parts.join("; ")}`
648
636
  : " — stale cache, run `ouro up` to refresh";
@@ -56,7 +56,6 @@ const os_cron_deps_1 = require("./os-cron-deps");
56
56
  const os_cron_1 = require("./os-cron");
57
57
  const daemon_tombstone_1 = require("./daemon-tombstone");
58
58
  const agent_config_check_1 = require("./agent-config-check");
59
- const drift_detection_1 = require("./drift-detection");
60
59
  const pulse_1 = require("./pulse");
61
60
  const socket_client_1 = require("./socket-client");
62
61
  const bundle_manifest_1 = require("../../mind/bundle-manifest");
@@ -235,33 +234,6 @@ function buildDaemonHealthState() {
235
234
  ...degradedComponents.map((entry) => ({ ...entry })),
236
235
  ...agentDegradedComponents,
237
236
  ];
238
- // Layer 4: probe each enabled agent for drift between agent.json
239
- // (intent) and state/providers.json (observed binding). The result is
240
- // a single boolean — driftDetected — that downgrades the rollup from
241
- // healthy to partial when true. Per-finding detail is consumed by
242
- // render-side surfaces (inner-status, --no-repair summary) and by
243
- // Layer 3 RepairGuide; the rollup itself only cares about presence.
244
- // Best-effort: a single agent's read failure is not blocking — the
245
- // function returns "no drift detected for that agent" and continues.
246
- const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
247
- const drift = [];
248
- for (const snapshot of snapshots) {
249
- try {
250
- const inputs = (0, drift_detection_1.loadDriftInputsForAgent)(bundlesRoot, snapshot.name);
251
- const findings = (0, drift_detection_1.detectProviderBindingDrift)({
252
- agentName: snapshot.name,
253
- agentJson: inputs.agentJson,
254
- providerState: inputs.providerState,
255
- });
256
- if (findings.length > 0) {
257
- drift.push(...findings);
258
- }
259
- }
260
- catch {
261
- // best-effort: continue scanning
262
- }
263
- }
264
- const driftDetected = drift.length > 0;
265
237
  // Layer 1 rollup: project per-agent snapshots into the minimal
266
238
  // AgentRollupInput shape and let computeDaemonRollup decide. The
267
239
  // input is "every enabled agent" — managedAgents was filtered via
@@ -284,7 +256,6 @@ function buildDaemonHealthState() {
284
256
  })),
285
257
  bootstrapDegraded: degradedComponents,
286
258
  safeMode: false,
287
- driftDetected,
288
259
  });
289
260
  return {
290
261
  status: rollupStatus,
@@ -294,7 +265,6 @@ function buildDaemonHealthState() {
294
265
  uptimeSeconds: Math.floor(process.uptime()),
295
266
  safeMode: null,
296
267
  degraded,
297
- drift,
298
268
  agents: Object.fromEntries(snapshots.map((snapshot) => [
299
269
  snapshot.name,
300
270
  {
@@ -158,12 +158,6 @@ function readHealth(healthPath) {
158
158
  parsed.habits === null) {
159
159
  return null;
160
160
  }
161
- // `drift` is required in DaemonHealthState but absent from cached
162
- // health files written by pre-Layer-4 daemons. Tolerate that legacy
163
- // shape by defaulting to []; the rest of the file is still valid.
164
- const drift = Array.isArray(parsed.drift)
165
- ? parsed.drift
166
- : [];
167
161
  return {
168
162
  status: parsed.status,
169
163
  mode: parsed.mode,
@@ -172,7 +166,6 @@ function readHealth(healthPath) {
172
166
  uptimeSeconds: parsed.uptimeSeconds,
173
167
  safeMode: parsed.safeMode,
174
168
  degraded: parsed.degraded,
175
- drift,
176
169
  agents: parsed.agents,
177
170
  habits: parsed.habits,
178
171
  };
@@ -50,8 +50,7 @@ function computeDaemonRollup(input) {
50
50
  // healthy vs partial.
51
51
  const hasUnhealthyAgent = notServing > 0;
52
52
  const hasBootstrapDegraded = input.bootstrapDegraded.length > 0;
53
- const hasDrift = input.driftDetected === true;
54
- if (hasUnhealthyAgent || hasBootstrapDegraded || hasDrift) {
53
+ if (hasUnhealthyAgent || hasBootstrapDegraded) {
55
54
  return "partial";
56
55
  }
57
56
  return "healthy";