@ouro.bot/cli 0.1.0-alpha.412 → 0.1.0-alpha.414

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,24 @@
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.414",
6
+ "changes": [
7
+ "`openai-codex` live checks now use the same Responses API request shape as real Codex turns, so `ouro up` and `ouro auth verify` stop reporting false `400 status code (no body)` failures for healthy GPT-5.4 credentials.",
8
+ "`ouro up` now detects daemon roster drift when enabled agent bundles change on disk and restarts the stale daemon, which removes ghost agents like a locally-removed `ouroboros` from the repair queue instead of pretending they still live here.",
9
+ "Bundle skeleton contract coverage now validates whichever local agent bundles actually exist, so one-agent machines can keep `slugger` installed without failing the full test suite just because `ouroboros` was intentionally removed.",
10
+ "Expanded Codex provider runtime coverage to exercise the real turn path, preserved output-item turn state, runtime error classification, and the ping callback bundle, which keeps the global 100% coverage gate green for this repair.",
11
+ "`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the Codex live-check and daemon-roster drift repair release."
12
+ ]
13
+ },
14
+ {
15
+ "version": "0.1.0-alpha.413",
16
+ "changes": [
17
+ "`ouro up` now shows real-time per-agent progress during provider checks -- the spinner displays which agent and vault operation is in progress (`slugger: reading vault items...`) instead of a static label with no detail.",
18
+ "Added `onProgress` callback to `refreshProviderCredentialPool` and `updateDetail()` method to `UpProgress` for sub-step spinner detail, threaded through the full `executeUpCommand` -> `checkAgentProviders` -> provider health check chain.",
19
+ "`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the progress threading release."
20
+ ]
21
+ },
4
22
  {
5
23
  "version": "0.1.0-alpha.412",
6
24
  "changes": [
@@ -332,7 +332,7 @@ async function checkAgentConfigWithProviderHealth(agentName, bundlesRoot, deps =
332
332
  if (stateResult.disabled)
333
333
  return { ok: true };
334
334
  const ping = deps.pingProvider ?? (await Promise.resolve().then(() => __importStar(require("../provider-ping")))).pingProvider;
335
- const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(agentName);
335
+ const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(agentName, deps.onProgress ? { onProgress: deps.onProgress } : undefined);
336
336
  const pingGroups = new Map();
337
337
  const lanes = ["outward", "inner"];
338
338
  for (const lane of lanes) {
@@ -98,13 +98,14 @@ const DEFAULT_DAEMON_STARTUP_POLL_INTERVAL_MS = 500;
98
98
  const DEFAULT_DAEMON_STARTUP_STABILITY_WINDOW_MS = 1_500;
99
99
  const DEFAULT_DAEMON_STARTUP_RETRY_LIMIT = 1;
100
100
  const DEFAULT_DAEMON_STARTUP_LOG_LINES = 10;
101
- async function checkAgentProviders(deps, agentsOverride) {
101
+ async function checkAgentProviders(deps, agentsOverride, onProgress) {
102
102
  const agents = agentsOverride ?? await listCliAgents(deps);
103
103
  const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
104
104
  const degraded = [];
105
105
  for (const agent of [...new Set(agents)]) {
106
106
  try {
107
- const result = await checkAgentProviderHealth(agent, bundlesRoot, deps);
107
+ onProgress?.(`${agent}: checking providers...`);
108
+ const result = await checkAgentProviderHealth(agent, bundlesRoot, deps, onProgress);
108
109
  if (result.ok)
109
110
  continue;
110
111
  const errorReason = result.error ?? "agent provider health check failed";
@@ -136,9 +137,14 @@ async function checkAgentProviders(deps, agentsOverride) {
136
137
  }
137
138
  return degraded;
138
139
  }
139
- async function checkAgentProviderHealth(agentName, bundlesRoot, deps) {
140
- if (deps.homeDir) {
141
- return (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agentName, bundlesRoot, { homeDir: deps.homeDir });
140
+ async function checkAgentProviderHealth(agentName, bundlesRoot, deps, onProgress) {
141
+ const liveDeps = {};
142
+ if (deps.homeDir)
143
+ liveDeps.homeDir = deps.homeDir;
144
+ if (onProgress)
145
+ liveDeps.onProgress = onProgress;
146
+ if (liveDeps.homeDir || liveDeps.onProgress) {
147
+ return (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agentName, bundlesRoot, liveDeps);
142
148
  }
143
149
  return (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agentName, bundlesRoot);
144
150
  }
@@ -151,8 +157,12 @@ async function listCliAgents(deps) {
151
157
  }
152
158
  return [];
153
159
  }
154
- async function checkAlreadyRunningAgentProviders(deps) {
155
- return checkAgentProviders(deps);
160
+ function managedAgentsSignature(agentNames) {
161
+ const unique = [...new Set(agentNames.map((agent) => agent.trim()).filter((agent) => agent.length > 0))].sort();
162
+ return unique.length > 0 ? unique.join(",") : "(none)";
163
+ }
164
+ async function checkAlreadyRunningAgentProviders(deps, onProgress) {
165
+ return checkAgentProviders(deps, undefined, onProgress);
156
166
  }
157
167
  function readinessIssueFromDegraded(entry) {
158
168
  return entry.issue ?? (0, readiness_repair_1.genericReadinessIssue)({
@@ -261,6 +271,9 @@ async function ensureDaemonRunning(deps, options = {}) {
261
271
  const alive = options.initialAlive ?? await deps.checkSocketAlive(deps.socketPath);
262
272
  if (alive) {
263
273
  const localRuntime = (0, runtime_metadata_1.getRuntimeMetadata)();
274
+ const localManagedAgents = managedAgentsSignature((0, agent_discovery_1.listEnabledBundleAgents)({
275
+ bundlesRoot: deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)(),
276
+ }));
264
277
  let runningRuntimePromise = null;
265
278
  const fetchRunningRuntimeMetadata = async () => {
266
279
  runningRuntimePromise ??= (async () => {
@@ -271,6 +284,9 @@ async function ensureDaemonRunning(deps, options = {}) {
271
284
  lastUpdated: payload?.overview.lastUpdated ?? "unknown",
272
285
  repoRoot: payload?.overview.repoRoot ?? "unknown",
273
286
  configFingerprint: payload?.overview.configFingerprint ?? "unknown",
287
+ managedAgents: payload && payload.workers.length > 0
288
+ ? managedAgentsSignature(payload.workers.map((worker) => worker.agent))
289
+ : "unknown",
274
290
  };
275
291
  })();
276
292
  return runningRuntimePromise;
@@ -281,6 +297,7 @@ async function ensureDaemonRunning(deps, options = {}) {
281
297
  localLastUpdated: localRuntime.lastUpdated,
282
298
  localRepoRoot: localRuntime.repoRoot,
283
299
  localConfigFingerprint: localRuntime.configFingerprint,
300
+ localManagedAgents,
284
301
  fetchRunningVersion: async () => (await fetchRunningRuntimeMetadata()).version,
285
302
  fetchRunningRuntimeMetadata,
286
303
  stopDaemon: async () => {
@@ -2578,7 +2595,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
2578
2595
  let providerChecksAlreadyRun = false;
2579
2596
  if (!daemonAliveBeforeStart) {
2580
2597
  progress.startPhase("provider checks");
2581
- const preflightProviderDegraded = await checkAgentProviders(deps);
2598
+ const preflightProviderDegraded = await checkAgentProviders(deps, undefined, (msg) => progress.updateDetail(msg));
2582
2599
  providerChecksAlreadyRun = true;
2583
2600
  progress.completePhase("provider checks", providerRepairCountSummary(preflightProviderDegraded.length));
2584
2601
  if (preflightProviderDegraded.length > 0) {
@@ -2616,7 +2633,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
2616
2633
  }, { initialAlive: daemonAliveBeforeStart });
2617
2634
  if (!providerChecksAlreadyRun || daemonResult.alreadyRunning) {
2618
2635
  progress.startPhase("provider checks");
2619
- const providerDegraded = await checkAlreadyRunningAgentProviders(deps);
2636
+ const providerDegraded = await checkAlreadyRunningAgentProviders(deps, (msg) => progress.updateDetail(msg));
2620
2637
  daemonResult.stability = mergeStartupStability(daemonResult.stability, providerDegraded);
2621
2638
  progress.completePhase("provider checks", providerRepairCountSummary(providerDegraded.length));
2622
2639
  }
@@ -32,6 +32,7 @@ function normalizeRuntimeIdentity(value) {
32
32
  lastUpdated: typeof value.lastUpdated === "string" ? value.lastUpdated : "unknown",
33
33
  repoRoot: typeof value.repoRoot === "string" ? value.repoRoot : "unknown",
34
34
  configFingerprint: typeof value.configFingerprint === "string" ? value.configFingerprint : "unknown",
35
+ managedAgents: typeof value.managedAgents === "string" ? value.managedAgents : "unknown",
35
36
  };
36
37
  }
37
38
  async function readRunningRuntimeIdentity(deps) {
@@ -70,6 +71,16 @@ function collectRuntimeDriftReasons(local, running) {
70
71
  running: running.configFingerprint,
71
72
  });
72
73
  }
74
+ if (isKnownRuntimeValue(local.managedAgents)
75
+ && isKnownRuntimeValue(running.managedAgents)
76
+ && local.managedAgents !== running.managedAgents) {
77
+ reasons.push({
78
+ key: "managedAgents",
79
+ label: "managed agents",
80
+ local: local.managedAgents,
81
+ running: running.managedAgents,
82
+ });
83
+ }
73
84
  return reasons;
74
85
  }
75
86
  function formatRuntimeValue(reason) {
@@ -87,6 +98,7 @@ async function ensureCurrentDaemonRuntime(deps) {
87
98
  lastUpdated: deps.localLastUpdated,
88
99
  repoRoot: deps.localRepoRoot,
89
100
  configFingerprint: deps.localConfigFingerprint,
101
+ managedAgents: deps.localManagedAgents,
90
102
  });
91
103
  try {
92
104
  const runningRuntime = await readRunningRuntimeIdentity(deps);
@@ -118,10 +130,12 @@ async function ensureCurrentDaemonRuntime(deps) {
118
130
  localLastUpdated: localRuntime.lastUpdated,
119
131
  localRepoRoot: localRuntime.repoRoot,
120
132
  localConfigFingerprint: localRuntime.configFingerprint,
133
+ localManagedAgents: localRuntime.managedAgents,
121
134
  runningVersion,
122
135
  runningLastUpdated: runningRuntime.lastUpdated,
123
136
  runningRepoRoot: runningRuntime.repoRoot,
124
137
  runningConfigFingerprint: runningRuntime.configFingerprint,
138
+ runningManagedAgents: runningRuntime.managedAgents,
125
139
  action: "stale_replace_failed",
126
140
  driftKeys: driftReasons.map((entry) => entry.key),
127
141
  reason,
@@ -153,10 +167,12 @@ async function ensureCurrentDaemonRuntime(deps) {
153
167
  localLastUpdated: localRuntime.lastUpdated,
154
168
  localRepoRoot: localRuntime.repoRoot,
155
169
  localConfigFingerprint: localRuntime.configFingerprint,
170
+ localManagedAgents: localRuntime.managedAgents,
156
171
  runningVersion,
157
172
  runningLastUpdated: runningRuntime.lastUpdated,
158
173
  runningRepoRoot: runningRuntime.repoRoot,
159
174
  runningConfigFingerprint: runningRuntime.configFingerprint,
175
+ runningManagedAgents: runningRuntime.managedAgents,
160
176
  action: "stale_restarted",
161
177
  driftKeys: driftReasons.map((entry) => entry.key),
162
178
  pid: started.pid ?? null,
@@ -179,10 +195,12 @@ async function ensureCurrentDaemonRuntime(deps) {
179
195
  localLastUpdated: localRuntime.lastUpdated,
180
196
  localRepoRoot: localRuntime.repoRoot,
181
197
  localConfigFingerprint: localRuntime.configFingerprint,
198
+ localManagedAgents: localRuntime.managedAgents,
182
199
  runningVersion,
183
200
  runningLastUpdated: runningRuntime.lastUpdated,
184
201
  runningRepoRoot: runningRuntime.repoRoot,
185
202
  runningConfigFingerprint: runningRuntime.configFingerprint,
203
+ runningManagedAgents: runningRuntime.managedAgents,
186
204
  action: "unknown_version",
187
205
  },
188
206
  });
@@ -206,6 +224,7 @@ async function ensureCurrentDaemonRuntime(deps) {
206
224
  localLastUpdated: localRuntime.lastUpdated,
207
225
  localRepoRoot: localRuntime.repoRoot,
208
226
  localConfigFingerprint: localRuntime.configFingerprint,
227
+ localManagedAgents: localRuntime.managedAgents,
209
228
  action: "status_lookup_failed",
210
229
  reason,
211
230
  },
@@ -228,6 +247,7 @@ async function ensureCurrentDaemonRuntime(deps) {
228
247
  localLastUpdated: localRuntime.lastUpdated,
229
248
  localRepoRoot: localRuntime.repoRoot,
230
249
  localConfigFingerprint: localRuntime.configFingerprint,
250
+ localManagedAgents: localRuntime.managedAgents,
231
251
  action: "already_current",
232
252
  },
233
253
  });
@@ -52,6 +52,16 @@ class UpProgress {
52
52
  return;
53
53
  this.write(label);
54
54
  }
55
+ /**
56
+ * Update the sub-step detail on the current spinner phase. Rendered as
57
+ * "label (Xs) -- detail" in TTY mode. No-op in non-TTY mode or when
58
+ * no phase is active.
59
+ */
60
+ updateDetail(detail) {
61
+ if (!this.isTTY || !this.currentPhase)
62
+ return;
63
+ this.currentPhase.detail = detail;
64
+ }
55
65
  /**
56
66
  * Mark the current phase as done. In non-TTY mode, immediately writes
57
67
  * a static line. Emits a nerves event for observability.
@@ -94,7 +104,8 @@ class UpProgress {
94
104
  const elapsedSec = (elapsed / 1000).toFixed(1);
95
105
  const frameIndex = Math.floor(elapsed / 80) % SPINNER_FRAMES.length;
96
106
  const spinner = SPINNER_FRAMES[frameIndex];
97
- lines.push(` ${BOLD}${spinner}${RESET} ${this.currentPhase.label} ${DIM}(${elapsedSec}s)${RESET}`);
107
+ const detailSuffix = this.currentPhase.detail ? ` \u2014 ${this.currentPhase.detail}` : "";
108
+ lines.push(` ${BOLD}${spinner}${RESET} ${this.currentPhase.label} ${DIM}(${elapsedSec}s)${detailSuffix}${RESET}`);
98
109
  }
99
110
  let output = "";
100
111
  if (this.prevLineCount > 0) {
@@ -224,6 +224,7 @@ function readProviderCredentialPool(agentName) {
224
224
  async function refreshProviderCredentialPool(agentName, options = {}) {
225
225
  try {
226
226
  const store = (0, credential_access_1.getCredentialStore)(agentName);
227
+ options.onProgress?.(`reading vault items for ${agentName}...`);
227
228
  const items = await store.list();
228
229
  const providers = {};
229
230
  let updatedAt = new Date(0).toISOString();
@@ -233,6 +234,7 @@ async function refreshProviderCredentialPool(agentName, options = {}) {
233
234
  const provider = item.domain.slice(VAULT_ITEM_PREFIX.length);
234
235
  if (!isAgentProvider(provider))
235
236
  continue;
237
+ options.onProgress?.(`reading ${provider} credentials...`);
236
238
  const raw = await store.getRawSecret(item.domain, "password");
237
239
  const payload = validateProviderCredentialPayload(JSON.parse(raw), provider);
238
240
  const record = recordFromPayload(payload);
@@ -240,6 +242,7 @@ async function refreshProviderCredentialPool(agentName, options = {}) {
240
242
  if (record.updatedAt > updatedAt)
241
243
  updatedAt = record.updatedAt;
242
244
  }
245
+ options.onProgress?.("parsing provider credentials...");
243
246
  const pool = {
244
247
  schemaVersion: 1,
245
248
  updatedAt,
@@ -20,6 +20,16 @@ const OPENAI_CODEX_AUTH_FAILURE_MARKERS = [
20
20
  "invalid bearer token",
21
21
  ];
22
22
  const OPENAI_CODEX_BACKEND_BASE_URL = "https://chatgpt.com/backend-api/codex";
23
+ const OPENAI_CODEX_PING_INPUT = [{ role: "user", content: "ping" }];
24
+ const OPENAI_CODEX_PING_CALLBACKS = {
25
+ onModelStart() { },
26
+ onModelStreamStart() { },
27
+ onTextChunk() { },
28
+ onReasoningChunk() { },
29
+ onToolStart() { },
30
+ onToolEnd() { },
31
+ onError() { },
32
+ };
23
33
  function getOpenAICodexAgentNameForGuidance() {
24
34
  return (0, identity_1.getAgentName)();
25
35
  }
@@ -82,6 +92,18 @@ function getChatGPTAccountIdFromToken(token) {
82
92
  return "";
83
93
  return accountId.trim();
84
94
  }
95
+ function createOpenAICodexResponsesParams(input, instructions, model, reasoningEffort) {
96
+ return {
97
+ model,
98
+ input,
99
+ instructions,
100
+ tools: [],
101
+ reasoning: { effort: reasoningEffort, summary: "detailed" },
102
+ stream: true,
103
+ store: false,
104
+ include: ["reasoning.encrypted_content"],
105
+ };
106
+ }
85
107
  function createOpenAICodexProviderRuntime(model, codexConfig = (0, config_1.getOpenAICodexConfig)()) {
86
108
  (0, runtime_1.emitNervesEvent)({
87
109
  component: "engine",
@@ -137,16 +159,8 @@ function createOpenAICodexProviderRuntime(model, codexConfig = (0, config_1.getO
137
159
  async streamTurn(request) {
138
160
  if (!nativeInput)
139
161
  this.resetTurnState(request.messages);
140
- const params = {
141
- model: this.model,
142
- input: nativeInput,
143
- instructions: nativeInstructions,
144
- tools: (0, streaming_1.toResponsesTools)(request.activeTools),
145
- reasoning: { effort: request.reasoningEffort ?? "medium", summary: "detailed" },
146
- stream: true,
147
- store: false,
148
- include: ["reasoning.encrypted_content"],
149
- };
162
+ const params = createOpenAICodexResponsesParams(nativeInput, nativeInstructions, this.model, request.reasoningEffort ?? "medium");
163
+ params.tools = (0, streaming_1.toResponsesTools)(request.activeTools);
150
164
  if (request.toolChoiceRequired)
151
165
  params.tool_choice = "required";
152
166
  try {
@@ -161,7 +175,7 @@ function createOpenAICodexProviderRuntime(model, codexConfig = (0, config_1.getO
161
175
  },
162
176
  /* v8 ignore start -- ping: tested via provider-ping.test.ts @preserve */
163
177
  async ping(signal) {
164
- await this.client.responses.create({ model: this.model, input: "ping", max_output_tokens: 16 }, { signal });
178
+ await (0, streaming_1.streamResponsesApi)(this.client, createOpenAICodexResponsesParams(OPENAI_CODEX_PING_INPUT, "", this.model, "medium"), OPENAI_CODEX_PING_CALLBACKS, signal);
165
179
  },
166
180
  /* v8 ignore stop */
167
181
  /* v8 ignore next 3 -- delegation: classification logic tested via classifyOpenAICodexError @preserve */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.412",
3
+ "version": "0.1.0-alpha.414",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",