@ouro.bot/cli 0.1.0-alpha.553 → 0.1.0-alpha.555

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.
@@ -41,13 +41,15 @@ const path = __importStar(require("path"));
41
41
  const identity_1 = require("../identity");
42
42
  const runtime_1 = require("../../nerves/runtime");
43
43
  const provider_models_1 = require("../provider-models");
44
- const machine_identity_1 = require("../machine-identity");
45
- const provider_state_1 = require("../provider-state");
46
44
  const provider_credentials_1 = require("../provider-credentials");
45
+ const provider_readiness_cache_1 = require("../provider-readiness-cache");
47
46
  const vault_unlock_1 = require("../../repertoire/vault-unlock");
48
47
  const readiness_repair_1 = require("./readiness-repair");
49
48
  const provider_ping_progress_1 = require("./provider-ping-progress");
50
- const drift_detection_1 = require("./drift-detection");
49
+ const LANES = [
50
+ { lane: "outward", facing: "humanFacing" },
51
+ { lane: "inner", facing: "agentFacing" },
52
+ ];
51
53
  function isAgentProvider(value) {
52
54
  return Object.prototype.hasOwnProperty.call(identity_1.PROVIDER_CREDENTIALS, value);
53
55
  }
@@ -57,6 +59,9 @@ function agentRootFor(agentName, bundlesRoot) {
57
59
  function configPathFor(agentName, bundlesRoot) {
58
60
  return path.join(agentRootFor(agentName, bundlesRoot), "agent.json");
59
61
  }
62
+ function laneForFacing(facing) {
63
+ return facing === "humanFacing" ? "outward" : "inner";
64
+ }
60
65
  function resolveFacingProvider(parsed, facing, agentName, agentJsonPath) {
61
66
  const raw = parsed[facing];
62
67
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
@@ -90,9 +95,13 @@ function resolveFacingProvider(parsed, facing, agentName, agentJsonPath) {
90
95
  },
91
96
  };
92
97
  }
93
- return { ok: true, selected: { facing, provider } };
98
+ const rawModel = raw.model;
99
+ const model = typeof rawModel === "string" && rawModel.trim().length > 0
100
+ ? rawModel.trim()
101
+ : (0, provider_models_1.getDefaultModelForProvider)(provider);
102
+ return { ok: true, selected: { facing, lane: laneForFacing(facing), provider, model } };
94
103
  }
95
- function readAgentConfigForProviderState(agentName, bundlesRoot) {
104
+ function readAgentConfigForProviderCheck(agentName, bundlesRoot) {
96
105
  const agentJsonPath = configPathFor(agentName, bundlesRoot);
97
106
  let raw;
98
107
  try {
@@ -127,83 +136,25 @@ function readAgentConfigForProviderState(agentName, bundlesRoot) {
127
136
  }
128
137
  return { ok: true, disabled: false, agentJsonPath, parsed };
129
138
  }
130
- function readFacingForBootstrap(parsed, facing, agentName, agentJsonPath) {
131
- const providerResult = resolveFacingProvider(parsed, facing, agentName, agentJsonPath);
132
- if (!providerResult.ok) {
133
- return {
134
- ok: false,
135
- result: {
136
- ok: false,
137
- error: providerResult.result.error,
138
- fix: `Run 'ouro use --agent ${agentName} --lane ${facing === "humanFacing" ? "outward" : "inner"} --provider <provider> --model <model>' to configure this machine's provider binding.`,
139
- },
140
- };
141
- }
142
- const raw = parsed[facing];
143
- const model = typeof raw.model === "string" && raw.model.trim().length > 0
144
- ? raw.model.trim()
145
- : (0, provider_models_1.getDefaultModelForProvider)(providerResult.selected.provider);
146
- return { ok: true, provider: providerResult.selected.provider, model };
147
- }
148
- function bootstrapMissingProviderState(input) {
149
- const outward = readFacingForBootstrap(input.parsed, "humanFacing", input.agentName, input.agentJsonPath);
150
- if (!outward.ok)
151
- return { ok: false, error: outward.result.error, fix: outward.result.fix };
152
- const inner = readFacingForBootstrap(input.parsed, "agentFacing", input.agentName, input.agentJsonPath);
153
- if (!inner.ok)
154
- return { ok: false, error: inner.result.error, fix: inner.result.fix };
155
- const now = new Date();
156
- const homeDir = (0, provider_credentials_1.providerCredentialMachineHomeDir)(input.homeDir);
157
- const machine = (0, machine_identity_1.loadOrCreateMachineIdentity)({ homeDir, now: () => now });
158
- const state = (0, provider_state_1.bootstrapProviderStateFromAgentConfig)({
159
- machineId: machine.machineId,
160
- now,
161
- agentConfig: {
162
- humanFacing: { provider: outward.provider, model: outward.model },
163
- agentFacing: { provider: inner.provider, model: inner.model },
164
- },
165
- });
166
- const agentRoot = agentRootFor(input.agentName, input.bundlesRoot);
167
- (0, provider_state_1.writeProviderState)(agentRoot, state);
168
- (0, runtime_1.emitNervesEvent)({
169
- component: "daemon",
170
- event: "daemon.provider_state_bootstrapped",
171
- message: "bootstrapped local provider state from agent config",
172
- meta: { agent: input.agentName, agentRoot },
173
- });
174
- return { ok: true, agentRoot, state };
175
- }
176
- function readOrBootstrapProviderStateForCheck(agentName, bundlesRoot, deps = {}) {
177
- const configResult = readAgentConfigForProviderState(agentName, bundlesRoot);
139
+ function readProviderSelectionForCheck(agentName, bundlesRoot) {
140
+ const configResult = readAgentConfigForProviderCheck(agentName, bundlesRoot);
178
141
  if (!configResult.ok)
179
142
  return { ok: false, result: configResult.result };
180
143
  if (configResult.disabled)
181
144
  return { ok: true, disabled: true };
182
- const agentRoot = agentRootFor(agentName, bundlesRoot);
183
- const stateResult = (0, provider_state_1.readProviderState)(agentRoot);
184
- if (stateResult.ok) {
185
- return { ok: true, disabled: false, agentRoot, state: stateResult.state };
186
- }
187
- if (stateResult.reason === "invalid") {
188
- return {
189
- ok: false,
190
- result: {
191
- ok: false,
192
- error: `provider state for ${agentName} is invalid at ${stateResult.statePath}: ${stateResult.error}`,
193
- fix: `Run 'ouro use --agent ${agentName} --lane outward --provider <provider> --model <model> --force' to rewrite this machine's provider binding.`,
194
- },
195
- };
145
+ const bindings = {};
146
+ for (const { lane, facing } of LANES) {
147
+ const selected = resolveFacingProvider(configResult.parsed, facing, agentName, configResult.agentJsonPath);
148
+ if (!selected.ok)
149
+ return { ok: false, result: selected.result };
150
+ bindings[lane] = selected.selected;
196
151
  }
197
- const bootstrap = bootstrapMissingProviderState({
198
- agentName,
199
- bundlesRoot,
200
- parsed: configResult.parsed,
201
- agentJsonPath: configResult.agentJsonPath,
202
- homeDir: deps.homeDir,
203
- });
204
- if (!bootstrap.ok)
205
- return { ok: false, result: bootstrap };
206
- return { ok: true, disabled: false, agentRoot: bootstrap.agentRoot, state: bootstrap.state };
152
+ return {
153
+ ok: true,
154
+ disabled: false,
155
+ agentRoot: agentRootFor(agentName, bundlesRoot),
156
+ bindings,
157
+ };
207
158
  }
208
159
  function providerCredentialConfig(record) {
209
160
  return {
@@ -216,21 +167,6 @@ function pingAttemptCount(result) {
216
167
  return result.attempts.length;
217
168
  return undefined;
218
169
  }
219
- function writeLaneReadiness(input) {
220
- const binding = input.state.lanes[input.lane];
221
- const checkedAt = new Date().toISOString();
222
- input.state.updatedAt = checkedAt;
223
- input.state.readiness[input.lane] = {
224
- status: input.status,
225
- provider: binding.provider,
226
- model: binding.model,
227
- checkedAt,
228
- credentialRevision: input.credentialRevision,
229
- ...(input.error ? { error: input.error } : {}),
230
- ...(input.attempts !== undefined ? { attempts: input.attempts } : {}),
231
- };
232
- (0, provider_state_1.writeProviderState)(input.agentRoot, input.state);
233
- }
234
170
  function missingCredentialResult(agentName, lane, provider, model, credentialPath) {
235
171
  return {
236
172
  ok: false,
@@ -325,14 +261,14 @@ function laneAudienceLabel(lane) {
325
261
  function bindingLabel(binding) {
326
262
  return `${binding.provider} / ${binding.model}`;
327
263
  }
328
- function selectedProviderPlan(agentName, state) {
264
+ function selectedProviderPlan(agentName, bindings) {
329
265
  return [
330
- `${agentName}: checking the providers this agent uses right now`,
331
- ...["outward", "inner"].map((lane) => `- ${laneAudienceLabel(lane)}: ${bindingLabel(state.lanes[lane])}`),
266
+ `${agentName}: checking the providers in agent.json`,
267
+ ...LANES.map(({ lane }) => `- ${laneAudienceLabel(lane)}: ${bindingLabel(bindings[lane])}`),
332
268
  ].join("\n");
333
269
  }
334
- function selectedProvidersForState(state) {
335
- return [...new Set(["outward", "inner"].map((lane) => state.lanes[lane].provider))];
270
+ function selectedProvidersForBindings(bindings) {
271
+ return [...new Set(LANES.map(({ lane }) => bindings[lane].provider))];
336
272
  }
337
273
  function mapVaultRefreshProgress(agentName, onProgress) {
338
274
  return (message) => {
@@ -359,80 +295,52 @@ function providerPingSubject(agentName, lanes) {
359
295
  * checkAgentConfigWithProviderHealth(), which reads the agent vault and pings.
360
296
  */
361
297
  function checkAgentConfig(agentName, bundlesRoot) {
362
- const configResult = readAgentConfigForProviderState(agentName, bundlesRoot);
363
- if (!configResult.ok)
364
- return configResult.result;
365
- if (configResult.disabled)
298
+ const selectionResult = readProviderSelectionForCheck(agentName, bundlesRoot);
299
+ if (!selectionResult.ok)
300
+ return selectionResult.result;
301
+ if (selectionResult.disabled)
366
302
  return { ok: true };
367
- const outward = readFacingForBootstrap(configResult.parsed, "humanFacing", agentName, configResult.agentJsonPath);
368
- if (!outward.ok)
369
- return outward.result;
370
- const inner = readFacingForBootstrap(configResult.parsed, "agentFacing", agentName, configResult.agentJsonPath);
371
- if (!inner.ok)
372
- return inner.result;
373
303
  (0, runtime_1.emitNervesEvent)({
374
304
  component: "daemon",
375
305
  event: "daemon.agent_config_valid",
376
306
  message: "agent config validation passed",
377
307
  meta: {
378
308
  agent: agentName,
379
- providers: [...new Set([outward.provider, inner.provider])],
309
+ providers: selectedProvidersForBindings(selectionResult.bindings),
380
310
  liveProviderCheck: false,
381
311
  },
382
312
  });
383
313
  return { ok: true };
384
314
  }
385
315
  async function checkAgentConfigWithProviderHealth(agentName, bundlesRoot, deps = {}) {
386
- const stateResult = readOrBootstrapProviderStateForCheck(agentName, bundlesRoot, deps);
387
- if (!stateResult.ok)
388
- return stateResult.result;
389
- if (stateResult.disabled)
316
+ const selectionResult = readProviderSelectionForCheck(agentName, bundlesRoot);
317
+ if (!selectionResult.ok)
318
+ return selectionResult.result;
319
+ if (selectionResult.disabled)
390
320
  return { ok: true };
391
- // Layer 4 drift detection. Runs once per call after state setup so that
392
- // drift findings ride along regardless of ping outcome — consumers
393
- // (Layer 4 rollup, Layer 3 RepairGuide) want to see drift even when a
394
- // live-check is failing for unrelated reasons. Drift detection is pure
395
- // and never throws on a malformed agent.json: any read error here would
396
- // indicate the bundle disappeared between state setup and now (a
397
- // race), which we surface as `[]` rather than a hard failure.
398
- let driftFindings = [];
399
- try {
400
- const inputs = (0, drift_detection_1.loadDriftInputsForAgent)(bundlesRoot, agentName);
401
- driftFindings = (0, drift_detection_1.detectProviderBindingDrift)({
402
- agentName,
403
- agentJson: inputs.agentJson,
404
- providerState: inputs.providerState,
405
- });
406
- }
407
- catch {
408
- /* v8 ignore next 1 -- defensive race-window guard: agent.json went away after state setup. Tested via Unit 5 integration when at all. @preserve */
409
- driftFindings = [];
410
- }
411
- deps.onProgress?.(selectedProviderPlan(agentName, stateResult.state));
321
+ deps.onProgress?.(selectedProviderPlan(agentName, selectionResult.bindings));
412
322
  const ping = deps.pingProvider ?? (await Promise.resolve().then(() => __importStar(require("../provider-ping")))).pingProvider;
413
- const shouldRecordReadiness = deps.recordReadiness ?? true;
414
- const providers = selectedProvidersForState(stateResult.state);
323
+ const providers = selectedProvidersForBindings(selectionResult.bindings);
415
324
  const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(agentName, {
416
325
  ...(deps.onProgress ? { onProgress: mapVaultRefreshProgress(agentName, deps.onProgress) } : {}),
417
326
  providers,
418
327
  preserveCachedOnFailure: true,
419
328
  });
420
329
  const pingGroups = new Map();
421
- const lanes = ["outward", "inner"];
422
- for (const lane of lanes) {
423
- const binding = stateResult.state.lanes[lane];
330
+ for (const { lane } of LANES) {
331
+ const binding = selectionResult.bindings[lane];
424
332
  if (!poolResult.ok) {
425
333
  if (poolResult.reason === "missing") {
426
- return { ...missingCredentialResult(agentName, lane, binding.provider, binding.model, poolResult.poolPath), driftFindings };
334
+ return missingCredentialResult(agentName, lane, binding.provider, binding.model, poolResult.poolPath);
427
335
  }
428
- return { ...invalidPoolResult(agentName, lane, binding.provider, binding.model, {
429
- ...poolResult,
430
- reason: poolResult.reason,
431
- }), driftFindings };
336
+ return invalidPoolResult(agentName, lane, binding.provider, binding.model, {
337
+ ...poolResult,
338
+ reason: poolResult.reason,
339
+ });
432
340
  }
433
341
  const record = credentialRecordForLane(poolResult.pool, binding.provider);
434
342
  if (!record) {
435
- return { ...missingCredentialResult(agentName, lane, binding.provider, binding.model, poolResult.poolPath), driftFindings };
343
+ return missingCredentialResult(agentName, lane, binding.provider, binding.model, poolResult.poolPath);
436
344
  }
437
345
  const key = `${binding.provider}\0${binding.model}\0${record.revision}`;
438
346
  const group = pingGroups.get(key);
@@ -466,37 +374,37 @@ async function checkAgentConfigWithProviderHealth(agentName, bundlesRoot, deps =
466
374
  let firstFailure = null;
467
375
  for (const { group, result } of pingResults) {
468
376
  if (!result.ok) {
469
- if (shouldRecordReadiness) {
470
- for (const lane of group.lanes) {
471
- writeLaneReadiness({
472
- agentRoot: stateResult.agentRoot,
473
- state: stateResult.state,
474
- lane,
475
- status: "failed",
476
- credentialRevision: group.record.revision,
477
- error: result.message,
478
- attempts: pingAttemptCount(result),
479
- });
480
- }
481
- }
482
- firstFailure ??= failedPingResult(agentName, group.lanes[0], group.provider, group.model, result);
483
- continue;
484
- }
485
- if (shouldRecordReadiness) {
486
377
  for (const lane of group.lanes) {
487
- writeLaneReadiness({
488
- agentRoot: stateResult.agentRoot,
489
- state: stateResult.state,
378
+ (0, provider_readiness_cache_1.recordProviderLaneReadiness)({
379
+ agentName,
490
380
  lane,
491
- status: "ready",
381
+ provider: group.provider,
382
+ model: group.model,
492
383
  credentialRevision: group.record.revision,
384
+ status: "failed",
385
+ checkedAt: new Date().toISOString(),
386
+ error: result.message,
493
387
  attempts: pingAttemptCount(result),
494
388
  });
495
389
  }
390
+ firstFailure ??= failedPingResult(agentName, group.lanes[0], group.provider, group.model, result);
391
+ continue;
392
+ }
393
+ for (const lane of group.lanes) {
394
+ (0, provider_readiness_cache_1.recordProviderLaneReadiness)({
395
+ agentName,
396
+ lane,
397
+ provider: group.provider,
398
+ model: group.model,
399
+ credentialRevision: group.record.revision,
400
+ status: "ready",
401
+ checkedAt: new Date().toISOString(),
402
+ attempts: pingAttemptCount(result),
403
+ });
496
404
  }
497
405
  }
498
406
  if (firstFailure)
499
- return { ...firstFailure, driftFindings };
407
+ return firstFailure;
500
408
  (0, runtime_1.emitNervesEvent)({
501
409
  component: "daemon",
502
410
  event: "daemon.agent_config_valid",
@@ -505,8 +413,7 @@ async function checkAgentConfigWithProviderHealth(agentName, bundlesRoot, deps =
505
413
  agent: agentName,
506
414
  providers: [...new Set([...pingGroups.values()].map((group) => group.provider))],
507
415
  liveProviderCheck: true,
508
- driftCount: driftFindings.length,
509
416
  },
510
417
  });
511
- return { ok: true, driftFindings };
418
+ return { ok: true };
512
419
  }
@@ -68,7 +68,7 @@ function buildSystemPrompt(degraded) {
68
68
  "and the simplest fix the user can apply.",
69
69
  ].join("\n");
70
70
  }
71
- function buildUserMessage(degraded, logsTail, driftFindings = [], syncFindings = []) {
71
+ function buildUserMessage(degraded, logsTail, syncFindings = []) {
72
72
  const agentDetails = degraded
73
73
  .map((d) => `Agent: ${d.agent}\n Error: ${d.errorReason}\n Fix hint: ${d.fixHint}`)
74
74
  .join("\n\n");
@@ -80,13 +80,6 @@ function buildUserMessage(degraded, logsTail, driftFindings = [], syncFindings =
80
80
  "Recent daemon logs:",
81
81
  logsTail,
82
82
  ];
83
- // Layer 3: thread Layer 4's structured drift findings into the prompt as
84
- // a JSON block. The `diagnose-bootstrap-drift` skill (loaded into the
85
- // system prompt by `buildSystemPromptWithRepairGuide`) instructs the LLM
86
- // how to read this shape and what `ouro use` repair to propose.
87
- if (driftFindings.length > 0) {
88
- sections.push("", "driftFindings (DriftFinding[]):", "```json", JSON.stringify(driftFindings, null, 2), "```");
89
- }
90
83
  // Layer 3: thread Layer 2's structured sync-probe findings into the
91
84
  // prompt as a JSON block. `diagnose-broken-remote` (auth/404/network/
92
85
  // timeout-hard) and `diagnose-sync-blocked` (dirty/non-fast-forward/
@@ -158,7 +151,7 @@ async function tryAgenticDiagnosis(degraded, provider, deps) {
158
151
  const repoRoot = deps.repoRootOverride ?? (0, identity_1.getRepoRoot)();
159
152
  const repairGuide = loadRepairGuideContent(repoRoot);
160
153
  const systemPrompt = buildSystemPromptWithRepairGuide(degraded, repairGuide);
161
- const userMessage = buildUserMessage(degraded, logsTail, deps.driftFindings, deps.syncFindings);
154
+ const userMessage = buildUserMessage(degraded, logsTail, deps.syncFindings);
162
155
  const messages = [
163
156
  { role: "system", content: systemPrompt },
164
157
  { role: "user", content: userMessage },