@ouro.bot/cli 0.1.0-alpha.413 → 0.1.0-alpha.415

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.415",
6
+ "changes": [
7
+ "Transient vault errors (timeout, ECONNREFUSED, socket hang up) are now classified separately from vault-locked errors in `ouro up` provider checks -- operators see retry guidance instead of misleading unlock/replace/recover instructions.",
8
+ "Fix text across vault unlock, vault create, missing credential, and failed ping results tightened to concise single-line instructions.",
9
+ "`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the error classification release."
10
+ ]
11
+ },
12
+ {
13
+ "version": "0.1.0-alpha.414",
14
+ "changes": [
15
+ "`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.",
16
+ "`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.",
17
+ "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.",
18
+ "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.",
19
+ "`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the Codex live-check and daemon-roster drift repair release."
20
+ ]
21
+ },
4
22
  {
5
23
  "version": "0.1.0-alpha.413",
6
24
  "changes": [
@@ -233,7 +233,7 @@ function missingCredentialResult(agentName, lane, provider, model, credentialPat
233
233
  return {
234
234
  ok: false,
235
235
  error: `${lane} provider ${provider} model ${model} has no credentials in ${agentName}'s vault at ${credentialPath}`,
236
- fix: `Run 'ouro auth --agent ${agentName} --provider ${provider}' to authenticate this machine, or run 'ouro use --agent ${agentName} --lane ${lane} --provider <provider> --model <model>' to choose a working provider/model.`,
236
+ fix: `Run 'ouro auth --agent ${agentName} --provider ${provider}' to authenticate.`,
237
237
  issue: (0, readiness_repair_1.providerCredentialMissingIssue)({
238
238
  agentName,
239
239
  lane,
@@ -243,7 +243,21 @@ function missingCredentialResult(agentName, lane, provider, model, credentialPat
243
243
  }),
244
244
  };
245
245
  }
246
+ function isTransientVaultError(error) {
247
+ const normalized = error.toLowerCase();
248
+ return (normalized.includes("timed out") ||
249
+ normalized.includes("econnrefused") ||
250
+ normalized.includes("socket hang up") ||
251
+ normalized.includes("etimedout"));
252
+ }
246
253
  function invalidPoolResult(agentName, lane, provider, model, pool) {
254
+ if (pool.reason === "unavailable" && isTransientVaultError(pool.error)) {
255
+ return {
256
+ ok: false,
257
+ error: `${lane} provider ${provider} model ${model} cannot read provider credentials from ${agentName}'s vault: ${pool.error}`,
258
+ fix: `Vault read timed out -- this usually resolves on retry. Run 'ouro up' again.`,
259
+ };
260
+ }
247
261
  if (pool.reason === "unavailable" && isVaultLockedError(pool.error)) {
248
262
  return {
249
263
  ok: false,
@@ -284,7 +298,7 @@ function failedPingResult(agentName, lane, provider, model, result) {
284
298
  return {
285
299
  ok: false,
286
300
  error: `${lane} provider ${provider} model ${model} failed live check: ${result.message}`,
287
- fix: `Run 'ouro auth --agent ${agentName} --provider ${provider}' to refresh credentials, or run 'ouro use --agent ${agentName} --lane ${lane} --provider <provider> --model <model>' to switch this lane.`,
301
+ fix: `Run 'ouro auth --agent ${agentName} --provider ${provider}' to refresh credentials.`,
288
302
  issue: (0, readiness_repair_1.providerLiveCheckFailedIssue)({
289
303
  agentName,
290
304
  lane,
@@ -157,6 +157,10 @@ async function listCliAgents(deps) {
157
157
  }
158
158
  return [];
159
159
  }
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
+ }
160
164
  async function checkAlreadyRunningAgentProviders(deps, onProgress) {
161
165
  return checkAgentProviders(deps, undefined, onProgress);
162
166
  }
@@ -267,6 +271,9 @@ async function ensureDaemonRunning(deps, options = {}) {
267
271
  const alive = options.initialAlive ?? await deps.checkSocketAlive(deps.socketPath);
268
272
  if (alive) {
269
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
+ }));
270
277
  let runningRuntimePromise = null;
271
278
  const fetchRunningRuntimeMetadata = async () => {
272
279
  runningRuntimePromise ??= (async () => {
@@ -277,6 +284,9 @@ async function ensureDaemonRunning(deps, options = {}) {
277
284
  lastUpdated: payload?.overview.lastUpdated ?? "unknown",
278
285
  repoRoot: payload?.overview.repoRoot ?? "unknown",
279
286
  configFingerprint: payload?.overview.configFingerprint ?? "unknown",
287
+ managedAgents: payload && payload.workers.length > 0
288
+ ? managedAgentsSignature(payload.workers.map((worker) => worker.agent))
289
+ : "unknown",
280
290
  };
281
291
  })();
282
292
  return runningRuntimePromise;
@@ -287,6 +297,7 @@ async function ensureDaemonRunning(deps, options = {}) {
287
297
  localLastUpdated: localRuntime.lastUpdated,
288
298
  localRepoRoot: localRuntime.repoRoot,
289
299
  localConfigFingerprint: localRuntime.configFingerprint,
300
+ localManagedAgents,
290
301
  fetchRunningVersion: async () => (await fetchRunningRuntimeMetadata()).version,
291
302
  fetchRunningRuntimeMetadata,
292
303
  stopDaemon: async () => {
@@ -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
  });
@@ -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 */
@@ -101,13 +101,8 @@ function missingSecureStoreMessage(config) {
101
101
  : "Run `ouro vault unlock --store plaintext-file` to store the vault unlock secret in a chmod 0600 local file.",
102
102
  ].join("\n");
103
103
  }
104
- function vaultUnlockReplaceRecoverFix(agentName, nextStep = "Then run 'ouro up' again.") {
105
- return [
106
- `Run 'ouro vault unlock --agent ${agentName}' if you have the saved vault unlock secret.`,
107
- `If this agent predates vault auth or nobody saved the unlock secret, run 'ouro vault replace --agent ${agentName}' to create a new empty vault, then re-auth/re-enter credentials.`,
108
- `If you do have a local JSON credential export, run 'ouro vault recover --agent ${agentName} --from <json>' to create the agent vault and import it.`,
109
- nextStep,
110
- ].join(" ");
104
+ function vaultUnlockReplaceRecoverFix(agentName, _nextStep) {
105
+ return `Run 'ouro vault unlock --agent ${agentName}' or 'ouro vault replace --agent ${agentName}' if the secret is lost.`;
111
106
  }
112
107
  function credentialVaultNotConfiguredError(agentName, configPath) {
113
108
  return (`${CREDENTIAL_VAULT_NOT_CONFIGURED_PREFIX}${configPath}. ` +
@@ -116,12 +111,8 @@ function credentialVaultNotConfiguredError(agentName, configPath) {
116
111
  function isCredentialVaultNotConfiguredError(message) {
117
112
  return message.includes(CREDENTIAL_VAULT_NOT_CONFIGURED_PREFIX);
118
113
  }
119
- function vaultCreateRecoverFix(agentName, nextStep = "Then run 'ouro up' again.") {
120
- return [
121
- `Run 'ouro vault create --agent ${agentName}' to create this agent's vault.`,
122
- `If you still have a local JSON credential export from an earlier alpha, run 'ouro vault recover --agent ${agentName} --from <json>' instead.`,
123
- nextStep,
124
- ].join(" ");
114
+ function vaultCreateRecoverFix(agentName, _nextStep) {
115
+ return `Run 'ouro vault create --agent ${agentName}' to set up this agent's vault.`;
125
116
  }
126
117
  function vaultUnlockSecretStrengthIssues(secret) {
127
118
  const issues = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.413",
3
+ "version": "0.1.0-alpha.415",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",