@ouro.bot/cli 0.1.0-alpha.337 → 0.1.0-alpha.339

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,19 @@
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.339",
6
+ "changes": [
7
+ "`ouro config model` now validates GitHub Copilot model probes through the shared provider ping module, keeping provider health request shapes in one place.",
8
+ "Provider startup checks, auth verification, provider discovery, and model validation now share `src/heart/provider-ping.ts` as the canonical home for live provider ping logic."
9
+ ]
10
+ },
11
+ {
12
+ "version": "0.1.0-alpha.338",
13
+ "changes": [
14
+ "fix(mind): instruct agent not to echo timestamp tags in responses"
15
+ ]
16
+ },
4
17
  {
5
18
  "version": "0.1.0-alpha.337",
6
19
  "changes": [
@@ -42,7 +42,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
42
42
  exports.mergeStartupStability = mergeStartupStability;
43
43
  exports.ensureDaemonRunning = ensureDaemonRunning;
44
44
  exports.listGithubCopilotModels = listGithubCopilotModels;
45
- exports.pingGithubCopilotModel = pingGithubCopilotModel;
46
45
  exports.runOuroCli = runOuroCli;
47
46
  const child_process_1 = require("child_process");
48
47
  const crypto_1 = require("crypto");
@@ -78,6 +77,7 @@ const agentic_repair_1 = require("./agentic-repair");
78
77
  const startup_tui_1 = require("./startup-tui");
79
78
  const stale_bundle_prune_1 = require("./stale-bundle-prune");
80
79
  const up_progress_1 = require("./up-progress");
80
+ const provider_ping_1 = require("../provider-ping");
81
81
  // ── ensureDaemonRunning ──
82
82
  const DEFAULT_DAEMON_STARTUP_TIMEOUT_MS = 10_000;
83
83
  const DEFAULT_DAEMON_STARTUP_POLL_INTERVAL_MS = 500;
@@ -410,49 +410,6 @@ async function listGithubCopilotModels(baseUrl, token, fetchImpl = fetch) {
410
410
  });
411
411
  /* v8 ignore stop */
412
412
  }
413
- async function pingGithubCopilotModel(baseUrl, token, model, fetchImpl = fetch) {
414
- const base = baseUrl.replace(/\/+$/, "");
415
- const isClaude = model.startsWith("claude");
416
- const url = isClaude ? `${base}/chat/completions` : `${base}/responses`;
417
- const body = isClaude
418
- ? JSON.stringify({ model, messages: [{ role: "user", content: "ping" }], max_tokens: 1 })
419
- : JSON.stringify({ model, input: "ping", max_output_tokens: 16 });
420
- try {
421
- const response = await fetchImpl(url, {
422
- method: "POST",
423
- headers: {
424
- Authorization: `Bearer ${token}`,
425
- "Content-Type": "application/json",
426
- },
427
- body,
428
- });
429
- if (response.ok)
430
- return { ok: true };
431
- let detail = `HTTP ${response.status}`;
432
- try {
433
- const json = await response.json();
434
- /* v8 ignore start -- error format parsing: all branches tested via config-models.test.ts @preserve */
435
- if (typeof json.error === "string")
436
- detail = json.error;
437
- else if (typeof json.error === "object" && json.error !== null) {
438
- const errObj = json.error;
439
- if (typeof errObj.message === "string")
440
- detail = errObj.message;
441
- }
442
- else if (typeof json.message === "string")
443
- detail = json.message;
444
- /* v8 ignore stop */
445
- }
446
- catch {
447
- // response body not JSON — keep HTTP status
448
- }
449
- return { ok: false, error: detail };
450
- }
451
- catch (err) {
452
- /* v8 ignore next -- defensive: fetch errors are always Error instances @preserve */
453
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
454
- }
455
- }
456
413
  // ── Provider credential verification ──
457
414
  /* v8 ignore next 3 -- only called from auth.switch inside integration block @preserve */
458
415
  function hasStoredCredentials(provider, providerSecrets) {
@@ -1910,7 +1867,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
1910
1867
  // Catalog validation failed — fall through to ping test
1911
1868
  }
1912
1869
  // Ping test: verify the model actually works before switching
1913
- const pingResult = await pingGithubCopilotModel(ghConfig.baseUrl, ghConfig.githubToken, command.modelName, fetchFn);
1870
+ const pingResult = await (0, provider_ping_1.pingGithubCopilotModel)(ghConfig.baseUrl, ghConfig.githubToken, command.modelName, fetchFn);
1914
1871
  if (!pingResult.ok) {
1915
1872
  const message = `model '${command.modelName}' ping failed: ${pingResult.error}\nrun \`ouro config models --agent ${command.agent}\` to see available models.`;
1916
1873
  deps.writeStdout(message);
@@ -23,7 +23,8 @@ var cli_exec_1 = require("./cli-exec");
23
23
  Object.defineProperty(exports, "runOuroCli", { enumerable: true, get: function () { return cli_exec_1.runOuroCli; } });
24
24
  Object.defineProperty(exports, "ensureDaemonRunning", { enumerable: true, get: function () { return cli_exec_1.ensureDaemonRunning; } });
25
25
  Object.defineProperty(exports, "listGithubCopilotModels", { enumerable: true, get: function () { return cli_exec_1.listGithubCopilotModels; } });
26
- Object.defineProperty(exports, "pingGithubCopilotModel", { enumerable: true, get: function () { return cli_exec_1.pingGithubCopilotModel; } });
26
+ var provider_ping_1 = require("../provider-ping");
27
+ Object.defineProperty(exports, "pingGithubCopilotModel", { enumerable: true, get: function () { return provider_ping_1.pingGithubCopilotModel; } });
27
28
  // ── Defaults ──
28
29
  var cli_defaults_1 = require("./cli-defaults");
29
30
  Object.defineProperty(exports, "createDefaultOuroCliDeps", { enumerable: true, get: function () { return cli_defaults_1.createDefaultOuroCliDeps; } });
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.sanitizeErrorMessage = sanitizeErrorMessage;
4
+ exports.pingGithubCopilotModel = pingGithubCopilotModel;
4
5
  exports.pingProvider = pingProvider;
5
6
  exports.runHealthInventory = runHealthInventory;
6
7
  const identity_1 = require("./identity");
@@ -13,6 +14,10 @@ const auth_flow_1 = require("./auth/auth-flow");
13
14
  const provider_models_1 = require("./provider-models");
14
15
  const runtime_1 = require("../nerves/runtime");
15
16
  const PING_TIMEOUT_MS = 10_000;
17
+ const PING_PROMPT = "ping";
18
+ const CHAT_PING_MAX_TOKENS = 1;
19
+ const RESPONSE_PING_MAX_OUTPUT_TOKENS = 16;
20
+ const ANTHROPIC_SETUP_PING_MODEL = "claude-haiku-4-5-20251001";
16
21
  const DEFAULT_AZURE_API_VERSION = "2025-04-01-preview";
17
22
  const PING_CALLBACKS = {
18
23
  onModelStart() { },
@@ -23,6 +28,15 @@ const PING_CALLBACKS = {
23
28
  onToolEnd() { },
24
29
  onError() { },
25
30
  };
31
+ function createPingMessages() {
32
+ return [{ role: "user", content: PING_PROMPT }];
33
+ }
34
+ function createChatPingRequest(model) {
35
+ return { model, max_tokens: CHAT_PING_MAX_TOKENS, messages: createPingMessages() };
36
+ }
37
+ function createResponsePingRequest(model) {
38
+ return { model, input: PING_PROMPT, max_output_tokens: RESPONSE_PING_MAX_OUTPUT_TOKENS };
39
+ }
26
40
  /**
27
41
  * Strip raw JSON/HTML API response bodies from error messages.
28
42
  * SDK errors often include the full response: "400 {"type":"error",...}" or "403 <html>...".
@@ -53,6 +67,49 @@ function sanitizeErrorMessage(message) {
53
67
  // Already clean (e.g., "401 Provided authentication token is expired.")
54
68
  return message;
55
69
  }
70
+ async function pingGithubCopilotModel(baseUrl, token, model, fetchImpl = fetch) {
71
+ const base = baseUrl.replace(/\/+$/, "");
72
+ const isClaude = model.startsWith("claude");
73
+ const url = isClaude ? `${base}/chat/completions` : `${base}/responses`;
74
+ const body = isClaude
75
+ ? JSON.stringify(createChatPingRequest(model))
76
+ : JSON.stringify(createResponsePingRequest(model));
77
+ try {
78
+ const response = await fetchImpl(url, {
79
+ method: "POST",
80
+ headers: {
81
+ Authorization: `Bearer ${token}`,
82
+ "Content-Type": "application/json",
83
+ },
84
+ body,
85
+ });
86
+ if (response.ok)
87
+ return { ok: true };
88
+ let detail = `HTTP ${response.status}`;
89
+ try {
90
+ const json = await response.json();
91
+ /* v8 ignore start -- error format parsing: all branches tested via config-models.test.ts @preserve */
92
+ if (typeof json.error === "string")
93
+ detail = json.error;
94
+ else if (typeof json.error === "object" && json.error !== null) {
95
+ const errObj = json.error;
96
+ if (typeof errObj.message === "string")
97
+ detail = errObj.message;
98
+ }
99
+ else if (typeof json.message === "string")
100
+ detail = json.message;
101
+ /* v8 ignore stop */
102
+ }
103
+ catch {
104
+ // response body not JSON — keep HTTP status
105
+ }
106
+ return { ok: false, error: detail };
107
+ }
108
+ catch (err) {
109
+ /* v8 ignore next -- defensive: fetch errors are always Error instances @preserve */
110
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
111
+ }
112
+ }
56
113
  function hasEmptyCredentials(provider, config) {
57
114
  const record = config;
58
115
  if (provider === "azure") {
@@ -117,11 +174,11 @@ async function pingProvider(provider, config) {
117
174
  // Override the beta header to exclude thinking (which requires a
118
175
  // thinking param in the request body).
119
176
  const client = runtime.client;
120
- await client.messages.create({ model: "claude-haiku-4-5-20251001", max_tokens: 1, messages: [{ role: "user", content: "ping" }] }, { signal: controller.signal, headers: { "anthropic-beta": "claude-code-20250219,oauth-2025-04-20" } });
177
+ await client.messages.create(createChatPingRequest(ANTHROPIC_SETUP_PING_MODEL), { signal: controller.signal, headers: { "anthropic-beta": "claude-code-20250219,oauth-2025-04-20" } });
121
178
  }
122
179
  else if (provider === "openai-codex") {
123
180
  await runtime.streamTurn({
124
- messages: [{ role: "user", content: "ping" }],
181
+ messages: createPingMessages(),
125
182
  activeTools: [],
126
183
  callbacks: PING_CALLBACKS,
127
184
  signal: controller.signal,
@@ -131,7 +188,7 @@ async function pingProvider(provider, config) {
131
188
  else {
132
189
  // OpenAI-compatible providers (azure, minimax, github-copilot)
133
190
  const client = runtime.client;
134
- await client.chat.completions.create({ model: runtime.model, max_tokens: 1, messages: [{ role: "user", content: "ping" }] }, { signal: controller.signal });
191
+ await client.chat.completions.create(createChatPingRequest(runtime.model), { signal: controller.signal });
135
192
  }
136
193
  return { ok: true };
137
194
  }
@@ -476,7 +476,11 @@ function dateSection() {
476
476
  const parts = Object.fromEntries(fmt.formatToParts(now).map((p) => [p.type, p.value]));
477
477
  /* v8 ignore next -- Intl hour-24 bug only triggers at midnight @preserve */
478
478
  const hour = parts.hour === "24" ? "00" : parts.hour;
479
- return `current date and time: ${parts.year}-${parts.month}-${parts.day} ${hour}:${parts.minute} ${parts.timeZoneName}`;
479
+ const datetime = `${parts.year}-${parts.month}-${parts.day} ${hour}:${parts.minute} ${parts.timeZoneName}`;
480
+ return [
481
+ `current date and time: ${datetime}`,
482
+ "messages in conversations may have a relative-time tag like [-5m] or [-2h] prepended to their content. these indicate how long ago each message was sent relative to now. they are metadata for your orientation only — never echo or reproduce them in your responses.",
483
+ ].join("\n");
480
484
  }
481
485
  function uniqueToolsByName(tools) {
482
486
  const seen = new Set();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.337",
3
+ "version": "0.1.0-alpha.339",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",