@ouro.bot/cli 0.1.0-alpha.336 → 0.1.0-alpha.338
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 +13 -0
- package/dist/heart/daemon/agent-config-check.js +203 -60
- package/dist/heart/daemon/cli-exec.js +65 -2
- package/dist/heart/daemon/daemon-entry.js +3 -3
- package/dist/heart/daemon/interactive-repair.js +24 -2
- package/dist/heart/daemon/process-manager.js +1 -1
- package/dist/heart/provider-ping.js +20 -10
- package/dist/heart/providers/anthropic.js +3 -5
- package/dist/heart/providers/azure.js +1 -2
- package/dist/heart/providers/github-copilot.js +1 -2
- package/dist/heart/providers/minimax.js +1 -2
- package/dist/heart/providers/openai-codex.js +1 -2
- package/dist/mind/prompt.js +5 -1
- package/package.json +1 -1
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.338",
|
|
6
|
+
"changes": [
|
|
7
|
+
"fix(mind): instruct agent not to echo timestamp tags in responses"
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"version": "0.1.0-alpha.337",
|
|
12
|
+
"changes": [
|
|
13
|
+
"`ouro up` now checks the selected human-facing and agent-facing providers for each discovered agent, so startup reports degraded agents immediately when a configured provider token is missing or fails a live health check.",
|
|
14
|
+
"Provider repair prompts now preserve the failed facing's provider, allowing agent-facing GitHub Copilot, MiniMax, Anthropic, Azure, or OpenAI Codex failures to route to the correct `ouro auth --agent <name> --provider <provider>` command."
|
|
15
|
+
]
|
|
16
|
+
},
|
|
4
17
|
{
|
|
5
18
|
"version": "0.1.0-alpha.336",
|
|
6
19
|
"changes": [
|
|
@@ -34,16 +34,88 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.checkAgentConfig = checkAgentConfig;
|
|
37
|
+
exports.checkAgentConfigWithProviderHealth = checkAgentConfigWithProviderHealth;
|
|
37
38
|
const fs = __importStar(require("fs"));
|
|
38
39
|
const path = __importStar(require("path"));
|
|
39
40
|
const identity_1 = require("../identity");
|
|
40
41
|
const runtime_1 = require("../../nerves/runtime");
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
function isAgentProvider(value) {
|
|
43
|
+
return Object.prototype.hasOwnProperty.call(identity_1.PROVIDER_CREDENTIALS, value);
|
|
44
|
+
}
|
|
45
|
+
function formatFacingList(facings) {
|
|
46
|
+
if (facings.length === 1)
|
|
47
|
+
return facings[0];
|
|
48
|
+
return `${facings.slice(0, -1).join(", ")} and ${facings[facings.length - 1]}`;
|
|
49
|
+
}
|
|
50
|
+
function selectedProviderMap(selectedProviders) {
|
|
51
|
+
const byProvider = new Map();
|
|
52
|
+
for (const selected of selectedProviders) {
|
|
53
|
+
const facings = byProvider.get(selected.provider) ?? [];
|
|
54
|
+
facings.push(selected.facing);
|
|
55
|
+
byProvider.set(selected.provider, facings);
|
|
56
|
+
}
|
|
57
|
+
return byProvider;
|
|
58
|
+
}
|
|
59
|
+
function resolveFacingProvider(parsed, facing, agentName, agentJsonPath) {
|
|
60
|
+
const raw = parsed[facing];
|
|
61
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
62
|
+
return {
|
|
63
|
+
ok: false,
|
|
64
|
+
result: {
|
|
65
|
+
ok: false,
|
|
66
|
+
error: `agent.json for '${agentName}' is missing ${facing}.provider`,
|
|
67
|
+
fix: `Add ${facing}: { provider, model } to ${agentJsonPath}. Valid providers: ${Object.keys(identity_1.PROVIDER_CREDENTIALS).join(", ")}`,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const provider = raw.provider;
|
|
72
|
+
if (typeof provider !== "string" || provider.length === 0) {
|
|
73
|
+
return {
|
|
74
|
+
ok: false,
|
|
75
|
+
result: {
|
|
76
|
+
ok: false,
|
|
77
|
+
error: `agent.json for '${agentName}' is missing ${facing}.provider`,
|
|
78
|
+
fix: `Set ${facing}.provider in ${agentJsonPath}. Valid providers: ${Object.keys(identity_1.PROVIDER_CREDENTIALS).join(", ")}`,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (!isAgentProvider(provider)) {
|
|
83
|
+
return {
|
|
84
|
+
ok: false,
|
|
85
|
+
result: {
|
|
86
|
+
ok: false,
|
|
87
|
+
error: `Unknown provider '${provider}' in ${facing}.provider for '${agentName}'`,
|
|
88
|
+
fix: `Set ${facing}.provider to one of: ${Object.keys(identity_1.PROVIDER_CREDENTIALS).join(", ")}`,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return { ok: true, selected: { facing, provider } };
|
|
93
|
+
}
|
|
94
|
+
function resolveSelectedProviders(parsed, agentName, agentJsonPath) {
|
|
95
|
+
const hasHumanFacing = parsed.humanFacing !== undefined;
|
|
96
|
+
const hasAgentFacing = parsed.agentFacing !== undefined;
|
|
97
|
+
if (!hasHumanFacing && !hasAgentFacing && typeof parsed.provider === "string") {
|
|
98
|
+
if (!isAgentProvider(parsed.provider)) {
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
result: {
|
|
102
|
+
ok: false,
|
|
103
|
+
error: `Unknown provider '${parsed.provider}' in agent.json for '${agentName}'`,
|
|
104
|
+
fix: `Set provider to one of: ${Object.keys(identity_1.PROVIDER_CREDENTIALS).join(", ")}`,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return { ok: true, selectedProviders: [{ facing: "provider", provider: parsed.provider }] };
|
|
109
|
+
}
|
|
110
|
+
const human = resolveFacingProvider(parsed, "humanFacing", agentName, agentJsonPath);
|
|
111
|
+
if (!human.ok)
|
|
112
|
+
return human;
|
|
113
|
+
const agent = resolveFacingProvider(parsed, "agentFacing", agentName, agentJsonPath);
|
|
114
|
+
if (!agent.ok)
|
|
115
|
+
return agent;
|
|
116
|
+
return { ok: true, selectedProviders: [human.selected, agent.selected] };
|
|
117
|
+
}
|
|
118
|
+
function readConfigCheckContext(agentName, bundlesRoot, secretsRoot) {
|
|
47
119
|
const agentJsonPath = path.join(bundlesRoot, `${agentName}.ouro`, "agent.json");
|
|
48
120
|
let raw;
|
|
49
121
|
try {
|
|
@@ -52,8 +124,11 @@ function checkAgentConfig(agentName, bundlesRoot, secretsRoot) {
|
|
|
52
124
|
catch {
|
|
53
125
|
return {
|
|
54
126
|
ok: false,
|
|
55
|
-
|
|
56
|
-
|
|
127
|
+
result: {
|
|
128
|
+
ok: false,
|
|
129
|
+
error: `agent.json not found at ${agentJsonPath}`,
|
|
130
|
+
fix: `Run 'ouro hatch ${agentName}' to create the agent bundle, or verify that ${bundlesRoot}/${agentName}.ouro/ exists.`,
|
|
131
|
+
},
|
|
57
132
|
};
|
|
58
133
|
}
|
|
59
134
|
let parsed;
|
|
@@ -63,40 +138,28 @@ function checkAgentConfig(agentName, bundlesRoot, secretsRoot) {
|
|
|
63
138
|
catch {
|
|
64
139
|
return {
|
|
65
140
|
ok: false,
|
|
66
|
-
|
|
67
|
-
|
|
141
|
+
result: {
|
|
142
|
+
ok: false,
|
|
143
|
+
error: `agent.json at ${agentJsonPath} contains invalid JSON`,
|
|
144
|
+
fix: `Open ${agentJsonPath} and fix the JSON syntax.`,
|
|
145
|
+
},
|
|
68
146
|
};
|
|
69
147
|
}
|
|
70
148
|
// Disabled agents are valid — they just won't run
|
|
71
149
|
if (parsed.enabled === false) {
|
|
72
|
-
return { ok: true };
|
|
73
|
-
}
|
|
74
|
-
// Resolve provider: humanFacing.provider > config.provider
|
|
75
|
-
let provider;
|
|
76
|
-
const humanFacing = parsed.humanFacing;
|
|
77
|
-
if (humanFacing && typeof humanFacing.provider === "string") {
|
|
78
|
-
provider = humanFacing.provider;
|
|
79
|
-
}
|
|
80
|
-
else if (typeof parsed.provider === "string") {
|
|
81
|
-
provider = parsed.provider;
|
|
82
|
-
}
|
|
83
|
-
if (!provider) {
|
|
84
150
|
return {
|
|
85
|
-
ok:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
ok: false,
|
|
94
|
-
error: `Unknown provider '${provider}' in agent.json for '${agentName}'`,
|
|
95
|
-
fix: `Set humanFacing.provider to one of: ${Object.keys(identity_1.PROVIDER_CREDENTIALS).join(", ")}`,
|
|
151
|
+
ok: true,
|
|
152
|
+
context: {
|
|
153
|
+
agentJsonPath,
|
|
154
|
+
secretsJsonPath: path.join(secretsRoot, agentName, "secrets.json"),
|
|
155
|
+
providers: {},
|
|
156
|
+
selectedProviders: [],
|
|
157
|
+
},
|
|
96
158
|
};
|
|
97
159
|
}
|
|
98
|
-
|
|
99
|
-
|
|
160
|
+
const selected = resolveSelectedProviders(parsed, agentName, agentJsonPath);
|
|
161
|
+
if (!selected.ok)
|
|
162
|
+
return selected;
|
|
100
163
|
const secretsJsonPath = path.join(secretsRoot, agentName, "secrets.json");
|
|
101
164
|
let secrets;
|
|
102
165
|
try {
|
|
@@ -104,46 +167,126 @@ function checkAgentConfig(agentName, bundlesRoot, secretsRoot) {
|
|
|
104
167
|
secrets = JSON.parse(secretsRaw);
|
|
105
168
|
}
|
|
106
169
|
catch {
|
|
170
|
+
const firstProvider = selected.selectedProviders[0].provider;
|
|
107
171
|
return {
|
|
108
172
|
ok: false,
|
|
109
|
-
|
|
110
|
-
|
|
173
|
+
result: {
|
|
174
|
+
ok: false,
|
|
175
|
+
error: `secrets.json not found or unreadable at ${secretsJsonPath}`,
|
|
176
|
+
fix: `Run 'ouro auth --agent ${agentName} --provider ${firstProvider}' to configure credentials, or create ${secretsJsonPath} with providers.${firstProvider} credentials.`,
|
|
177
|
+
},
|
|
111
178
|
};
|
|
112
179
|
}
|
|
113
180
|
const providers = secrets.providers;
|
|
114
|
-
|
|
115
|
-
|
|
181
|
+
if (!providers || typeof providers !== "object" || Array.isArray(providers)) {
|
|
182
|
+
const firstProvider = selected.selectedProviders[0].provider;
|
|
116
183
|
return {
|
|
117
184
|
ok: false,
|
|
118
|
-
|
|
119
|
-
|
|
185
|
+
result: {
|
|
186
|
+
ok: false,
|
|
187
|
+
error: `secrets.json for '${agentName}' is missing providers object`,
|
|
188
|
+
fix: `Run 'ouro auth --agent ${agentName} --provider ${firstProvider}' to configure credentials.`,
|
|
189
|
+
},
|
|
120
190
|
};
|
|
121
191
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
192
|
+
return {
|
|
193
|
+
ok: true,
|
|
194
|
+
context: {
|
|
195
|
+
agentJsonPath,
|
|
196
|
+
secretsJsonPath,
|
|
197
|
+
providers,
|
|
198
|
+
selectedProviders: selected.selectedProviders,
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function validateSelectedProviderSecrets(agentName, context) {
|
|
203
|
+
for (const [provider, facings] of selectedProviderMap(context.selectedProviders)) {
|
|
204
|
+
const desc = identity_1.PROVIDER_CREDENTIALS[provider];
|
|
205
|
+
const providerSecrets = context.providers[provider];
|
|
206
|
+
const selectedBy = formatFacingList(facings);
|
|
207
|
+
if (!providerSecrets) {
|
|
208
|
+
return {
|
|
209
|
+
ok: false,
|
|
210
|
+
error: `secrets.json for '${agentName}' is missing providers.${provider} section selected by ${selectedBy}`,
|
|
211
|
+
fix: `Run 'ouro auth --agent ${agentName} --provider ${provider}' to configure ${provider} credentials.`,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
// Azure special case: managed identity only needs endpoint + deployment.
|
|
215
|
+
if (provider === "azure") {
|
|
216
|
+
const hasEndpoint = typeof providerSecrets.endpoint === "string" && providerSecrets.endpoint.length > 0;
|
|
217
|
+
const hasDeployment = typeof providerSecrets.deployment === "string" && providerSecrets.deployment.length > 0;
|
|
218
|
+
const hasManagedId = typeof providerSecrets.managedIdentityClientId === "string" && providerSecrets.managedIdentityClientId.length > 0;
|
|
219
|
+
if (hasEndpoint && hasDeployment && hasManagedId) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const missing = desc.required.filter((field) => {
|
|
224
|
+
const val = providerSecrets[field];
|
|
225
|
+
return typeof val !== "string" || val.length === 0;
|
|
226
|
+
});
|
|
227
|
+
if (missing.length > 0) {
|
|
228
|
+
return {
|
|
229
|
+
ok: false,
|
|
230
|
+
error: `secrets.json for '${agentName}' is missing required ${provider} credentials selected by ${selectedBy}: ${missing.join(", ")}`,
|
|
231
|
+
fix: `Run 'ouro auth --agent ${agentName} --provider ${provider}' to set up ${provider} credentials, or add the missing fields to providers.${provider} in ${context.secretsJsonPath}.`,
|
|
232
|
+
};
|
|
129
233
|
}
|
|
130
234
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
});
|
|
135
|
-
if (missing.length > 0) {
|
|
136
|
-
return {
|
|
137
|
-
ok: false,
|
|
138
|
-
error: `secrets.json for '${agentName}' is missing required ${provider} credentials: ${missing.join(", ")}`,
|
|
139
|
-
fix: `Run 'ouro auth ${agentName}' to set up ${provider} credentials, or add the missing fields to providers.${provider} in ${secretsJsonPath}.`,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
235
|
+
return { ok: true };
|
|
236
|
+
}
|
|
237
|
+
function emitConfigValid(agentName, context, liveProviderCheck) {
|
|
142
238
|
(0, runtime_1.emitNervesEvent)({
|
|
143
239
|
component: "daemon",
|
|
144
240
|
event: "daemon.agent_config_valid",
|
|
145
241
|
message: "agent config validation passed",
|
|
146
|
-
meta: {
|
|
242
|
+
meta: {
|
|
243
|
+
agent: agentName,
|
|
244
|
+
providers: [...selectedProviderMap(context.selectedProviders).keys()],
|
|
245
|
+
liveProviderCheck,
|
|
246
|
+
},
|
|
147
247
|
});
|
|
248
|
+
}
|
|
249
|
+
function providerPingFailureResult(agentName, provider, facings, result) {
|
|
250
|
+
const selectedBy = formatFacingList(facings);
|
|
251
|
+
const authFix = `Run 'ouro auth --agent ${agentName} --provider ${provider}' to refresh credentials.`;
|
|
252
|
+
const verifyFix = `Run 'ouro auth verify --agent ${agentName} --provider ${provider}' for details.`;
|
|
253
|
+
return {
|
|
254
|
+
ok: false,
|
|
255
|
+
error: `selected provider ${provider} for ${selectedBy} failed health check: ${result.message}`,
|
|
256
|
+
fix: `${result.classification === "auth-failure" ? authFix : verifyFix} Or switch the affected facing to a working provider.`,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Pre-spawn validation: ensures agent.json exists and required secrets are present.
|
|
261
|
+
* Returns `{ ok: true }` when the agent is ready to run, or a descriptive error
|
|
262
|
+
* with an actionable fix message when something is missing.
|
|
263
|
+
*/
|
|
264
|
+
function checkAgentConfig(agentName, bundlesRoot, secretsRoot) {
|
|
265
|
+
const contextResult = readConfigCheckContext(agentName, bundlesRoot, secretsRoot);
|
|
266
|
+
if (!contextResult.ok)
|
|
267
|
+
return contextResult.result;
|
|
268
|
+
const context = contextResult.context;
|
|
269
|
+
const structural = validateSelectedProviderSecrets(agentName, context);
|
|
270
|
+
if (!structural.ok)
|
|
271
|
+
return structural;
|
|
272
|
+
emitConfigValid(agentName, context, false);
|
|
273
|
+
return { ok: true };
|
|
274
|
+
}
|
|
275
|
+
async function checkAgentConfigWithProviderHealth(agentName, bundlesRoot, secretsRoot, deps = {}) {
|
|
276
|
+
const contextResult = readConfigCheckContext(agentName, bundlesRoot, secretsRoot);
|
|
277
|
+
if (!contextResult.ok)
|
|
278
|
+
return contextResult.result;
|
|
279
|
+
const context = contextResult.context;
|
|
280
|
+
const structural = validateSelectedProviderSecrets(agentName, context);
|
|
281
|
+
if (!structural.ok)
|
|
282
|
+
return structural;
|
|
283
|
+
const ping = deps.pingProvider ?? (await Promise.resolve().then(() => __importStar(require("../provider-ping")))).pingProvider;
|
|
284
|
+
for (const [provider, facings] of selectedProviderMap(context.selectedProviders)) {
|
|
285
|
+
const result = await ping(provider, context.providers[provider]);
|
|
286
|
+
if (!result.ok) {
|
|
287
|
+
return providerPingFailureResult(agentName, provider, facings, result);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
emitConfigValid(agentName, context, true);
|
|
148
291
|
return { ok: true };
|
|
149
292
|
}
|
|
@@ -39,6 +39,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
39
39
|
};
|
|
40
40
|
})();
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.mergeStartupStability = mergeStartupStability;
|
|
42
43
|
exports.ensureDaemonRunning = ensureDaemonRunning;
|
|
43
44
|
exports.listGithubCopilotModels = listGithubCopilotModels;
|
|
44
45
|
exports.pingGithubCopilotModel = pingGithubCopilotModel;
|
|
@@ -69,6 +70,7 @@ const cli_parse_2 = require("./cli-parse");
|
|
|
69
70
|
const cli_help_1 = require("./cli-help");
|
|
70
71
|
const cli_render_1 = require("./cli-render");
|
|
71
72
|
const cli_defaults_1 = require("./cli-defaults");
|
|
73
|
+
const agent_config_check_1 = require("./agent-config-check");
|
|
72
74
|
const doctor_1 = require("./doctor");
|
|
73
75
|
const cli_render_doctor_1 = require("./cli-render-doctor");
|
|
74
76
|
const interactive_repair_1 = require("./interactive-repair");
|
|
@@ -82,6 +84,61 @@ const DEFAULT_DAEMON_STARTUP_POLL_INTERVAL_MS = 500;
|
|
|
82
84
|
const DEFAULT_DAEMON_STARTUP_STABILITY_WINDOW_MS = 1_500;
|
|
83
85
|
const DEFAULT_DAEMON_STARTUP_RETRY_LIMIT = 1;
|
|
84
86
|
const DEFAULT_DAEMON_STARTUP_LOG_LINES = 10;
|
|
87
|
+
async function checkAlreadyRunningAgentProviders(deps) {
|
|
88
|
+
const agents = await Promise.resolve(deps.listDiscoveredAgents ? deps.listDiscoveredAgents() : (0, cli_defaults_1.defaultListDiscoveredAgents)());
|
|
89
|
+
const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
|
|
90
|
+
const secretsRoot = deps.secretsRoot ?? path.join(os.homedir(), ".agentsecrets");
|
|
91
|
+
const degraded = [];
|
|
92
|
+
for (const agent of agents) {
|
|
93
|
+
try {
|
|
94
|
+
const result = await (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agent, bundlesRoot, secretsRoot);
|
|
95
|
+
if (result.ok)
|
|
96
|
+
continue;
|
|
97
|
+
const errorReason = result.error ?? "agent provider health check failed";
|
|
98
|
+
const fixHint = result.fix ?? "";
|
|
99
|
+
degraded.push({ agent, errorReason, fixHint });
|
|
100
|
+
(0, runtime_1.emitNervesEvent)({
|
|
101
|
+
level: "error",
|
|
102
|
+
component: "daemon",
|
|
103
|
+
event: "daemon.agent_config_invalid",
|
|
104
|
+
message: errorReason,
|
|
105
|
+
meta: { agent, fix: fixHint, source: "already-running-provider-check" },
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const errorReason = error instanceof Error ? error.message : String(error);
|
|
110
|
+
degraded.push({
|
|
111
|
+
agent,
|
|
112
|
+
errorReason,
|
|
113
|
+
fixHint: "Run 'ouro doctor' for diagnostics, then retry 'ouro up'.",
|
|
114
|
+
});
|
|
115
|
+
(0, runtime_1.emitNervesEvent)({
|
|
116
|
+
level: "error",
|
|
117
|
+
component: "daemon",
|
|
118
|
+
event: "daemon.agent_config_invalid",
|
|
119
|
+
message: errorReason,
|
|
120
|
+
meta: { agent, fix: "ouro doctor", source: "already-running-provider-check" },
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return degraded;
|
|
125
|
+
}
|
|
126
|
+
function mergeStartupStability(stability, extraDegraded) {
|
|
127
|
+
if (extraDegraded.length === 0)
|
|
128
|
+
return stability;
|
|
129
|
+
const degradedByAgent = new Map();
|
|
130
|
+
for (const entry of stability?.degraded ?? [])
|
|
131
|
+
degradedByAgent.set(entry.agent, entry);
|
|
132
|
+
for (const entry of extraDegraded)
|
|
133
|
+
degradedByAgent.set(entry.agent, entry);
|
|
134
|
+
const degraded = [...degradedByAgent.values()];
|
|
135
|
+
const stable = [];
|
|
136
|
+
for (const agent of stability?.stable ?? []) {
|
|
137
|
+
if (!degradedByAgent.has(agent))
|
|
138
|
+
stable.push(agent);
|
|
139
|
+
}
|
|
140
|
+
return { stable, degraded };
|
|
141
|
+
}
|
|
85
142
|
async function ensureDaemonRunning(deps) {
|
|
86
143
|
const readLatestDaemonStartupEvent = () => {
|
|
87
144
|
try {
|
|
@@ -1097,6 +1154,12 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
1097
1154
|
progress.announceStep?.(label);
|
|
1098
1155
|
},
|
|
1099
1156
|
});
|
|
1157
|
+
if (daemonResult.alreadyRunning) {
|
|
1158
|
+
progress.startPhase("provider checks");
|
|
1159
|
+
const providerDegraded = await checkAlreadyRunningAgentProviders(deps);
|
|
1160
|
+
daemonResult.stability = mergeStartupStability(daemonResult.stability, providerDegraded);
|
|
1161
|
+
progress.completePhase("provider checks", providerDegraded.length > 0 ? `${providerDegraded.length} degraded` : "ok");
|
|
1162
|
+
}
|
|
1100
1163
|
progress.end();
|
|
1101
1164
|
deps.writeStdout(daemonResult.message);
|
|
1102
1165
|
// Interactive repair for degraded agents (Unit 5) — skipped by --no-repair (Unit 6)
|
|
@@ -1159,9 +1222,9 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
1159
1222
|
runInteractiveRepair: interactive_repair_1.runInteractiveRepair,
|
|
1160
1223
|
promptInput: deps.promptInput ?? (async () => "n"),
|
|
1161
1224
|
writeStdout: deps.writeStdout,
|
|
1162
|
-
runAuthFlow: async (agent) => {
|
|
1225
|
+
runAuthFlow: async (agent, providerOverride) => {
|
|
1163
1226
|
const { config } = (0, auth_flow_1.readAgentConfigForAgent)(agent, deps.bundlesRoot);
|
|
1164
|
-
const provider = config.humanFacing.provider;
|
|
1227
|
+
const provider = providerOverride ?? config.humanFacing.provider;
|
|
1165
1228
|
/* v8 ignore next -- tests always inject runAuthFlow; default is for production @preserve */
|
|
1166
1229
|
const authRunner = deps.runAuthFlow ?? (await Promise.resolve().then(() => __importStar(require("../auth/auth-flow")))).runRuntimeAuthFlow;
|
|
1167
1230
|
await authRunner({ agentName: agent, provider, promptInput: deps.promptInput });
|
|
@@ -99,11 +99,11 @@ const processManager = new process_manager_1.DaemonProcessManager({
|
|
|
99
99
|
autoStart: true,
|
|
100
100
|
})),
|
|
101
101
|
existsSync: fs.existsSync,
|
|
102
|
-
/* v8 ignore next 4 -- wiring: delegates to
|
|
103
|
-
configCheck: (agent) => {
|
|
102
|
+
/* v8 ignore next 4 -- wiring: delegates to checkAgentConfigWithProviderHealth which has full unit tests @preserve */
|
|
103
|
+
configCheck: async (agent) => {
|
|
104
104
|
const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
|
|
105
105
|
const secretsRoot = path.join(os.homedir(), ".agentsecrets");
|
|
106
|
-
return (0, agent_config_check_1.
|
|
106
|
+
return (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agent, bundlesRoot, secretsRoot);
|
|
107
107
|
},
|
|
108
108
|
/* v8 ignore start -- pulse flush wiring: integration code; flushPulse itself has full unit tests @preserve */
|
|
109
109
|
onSnapshotChange: () => {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.runInteractiveRepair = runInteractiveRepair;
|
|
10
10
|
const runtime_1 = require("../../nerves/runtime");
|
|
11
|
+
const identity_1 = require("../identity");
|
|
11
12
|
function isCredentialIssue(degraded) {
|
|
12
13
|
const reason = degraded.errorReason.toLowerCase();
|
|
13
14
|
const hint = degraded.fixHint.toLowerCase();
|
|
@@ -16,6 +17,20 @@ function isCredentialIssue(degraded) {
|
|
|
16
17
|
function isConfigError(degraded) {
|
|
17
18
|
return degraded.fixHint.length > 0 && !isCredentialIssue(degraded);
|
|
18
19
|
}
|
|
20
|
+
function isAgentProvider(value) {
|
|
21
|
+
return Object.prototype.hasOwnProperty.call(identity_1.PROVIDER_CREDENTIALS, value);
|
|
22
|
+
}
|
|
23
|
+
function extractProviderFromFixHint(fixHint) {
|
|
24
|
+
const provider = fixHint.match(/--provider\s+([a-z0-9-]+)/)?.[1]
|
|
25
|
+
?? fixHint.match(/providers\.([a-z0-9-]+)/)?.[1];
|
|
26
|
+
if (!provider || !isAgentProvider(provider))
|
|
27
|
+
return undefined;
|
|
28
|
+
return provider;
|
|
29
|
+
}
|
|
30
|
+
function authCommandFor(degraded) {
|
|
31
|
+
const command = degraded.fixHint.match(/ouro auth[^\n.]+/)?.[0]?.trim();
|
|
32
|
+
return command && command.length > 0 ? command : `ouro auth --agent ${degraded.agent}`;
|
|
33
|
+
}
|
|
19
34
|
async function runInteractiveRepair(degraded, deps) {
|
|
20
35
|
(0, runtime_1.emitNervesEvent)({
|
|
21
36
|
level: "info",
|
|
@@ -30,10 +45,17 @@ async function runInteractiveRepair(degraded, deps) {
|
|
|
30
45
|
let repairsAttempted = false;
|
|
31
46
|
for (const entry of degraded) {
|
|
32
47
|
if (isCredentialIssue(entry)) {
|
|
33
|
-
const
|
|
48
|
+
const provider = extractProviderFromFixHint(entry.fixHint);
|
|
49
|
+
const authCommand = authCommandFor(entry);
|
|
50
|
+
const answer = await deps.promptInput(`run \`${authCommand}\` now? [y/n] `);
|
|
34
51
|
if (answer.toLowerCase() === "y") {
|
|
35
52
|
try {
|
|
36
|
-
|
|
53
|
+
if (provider) {
|
|
54
|
+
await deps.runAuthFlow(entry.agent, provider);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
await deps.runAuthFlow(entry.agent);
|
|
58
|
+
}
|
|
37
59
|
repairsAttempted = true;
|
|
38
60
|
}
|
|
39
61
|
catch (error) {
|
|
@@ -157,7 +157,7 @@ class DaemonProcessManager {
|
|
|
157
157
|
state.stopRequested = false;
|
|
158
158
|
state.snapshot.status = "starting";
|
|
159
159
|
if (this.configCheckFn) {
|
|
160
|
-
const result = this.configCheckFn(agent);
|
|
160
|
+
const result = await this.configCheckFn(agent);
|
|
161
161
|
if (!result.ok) {
|
|
162
162
|
state.snapshot.status = "crashed";
|
|
163
163
|
// Surface the error and fix to the snapshot so sibling agents can
|
|
@@ -13,6 +13,7 @@ const auth_flow_1 = require("./auth/auth-flow");
|
|
|
13
13
|
const provider_models_1 = require("./provider-models");
|
|
14
14
|
const runtime_1 = require("../nerves/runtime");
|
|
15
15
|
const PING_TIMEOUT_MS = 10_000;
|
|
16
|
+
const DEFAULT_AZURE_API_VERSION = "2025-04-01-preview";
|
|
16
17
|
const PING_CALLBACKS = {
|
|
17
18
|
onModelStart() { },
|
|
18
19
|
onModelStreamStart() { },
|
|
@@ -54,25 +55,34 @@ function sanitizeErrorMessage(message) {
|
|
|
54
55
|
}
|
|
55
56
|
function hasEmptyCredentials(provider, config) {
|
|
56
57
|
const record = config;
|
|
58
|
+
if (provider === "azure") {
|
|
59
|
+
const hasManagedIdentity = typeof record.endpoint === "string" && record.endpoint.length > 0 &&
|
|
60
|
+
typeof record.deployment === "string" && record.deployment.length > 0 &&
|
|
61
|
+
typeof record.managedIdentityClientId === "string" && record.managedIdentityClientId.length > 0;
|
|
62
|
+
if (hasManagedIdentity)
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
57
65
|
return identity_1.PROVIDER_CREDENTIALS[provider].required.some((key) => !record[key]);
|
|
58
66
|
}
|
|
59
|
-
function createRuntimeForPing(provider,
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
// and hatch so verification cannot drift to stale provider/model pairings.
|
|
67
|
+
function createRuntimeForPing(provider, config) {
|
|
68
|
+
// Use the same provider defaults as auth switch and hatch so verification
|
|
69
|
+
// cannot drift to stale provider/model pairings, and pass the checked
|
|
70
|
+
// credentials directly so daemon-side pings do not depend on --agent globals.
|
|
64
71
|
const model = (0, provider_models_1.getDefaultModelForProvider)(provider);
|
|
65
72
|
switch (provider) {
|
|
66
73
|
case "anthropic":
|
|
67
|
-
return (0, anthropic_1.createAnthropicProviderRuntime)(model);
|
|
74
|
+
return (0, anthropic_1.createAnthropicProviderRuntime)(model, config);
|
|
68
75
|
case "azure":
|
|
69
|
-
return (0, azure_1.createAzureProviderRuntime)(model
|
|
76
|
+
return (0, azure_1.createAzureProviderRuntime)(model, {
|
|
77
|
+
...config,
|
|
78
|
+
apiVersion: config.apiVersion ?? DEFAULT_AZURE_API_VERSION,
|
|
79
|
+
});
|
|
70
80
|
case "minimax":
|
|
71
|
-
return (0, minimax_1.createMinimaxProviderRuntime)(model);
|
|
81
|
+
return (0, minimax_1.createMinimaxProviderRuntime)(model, config);
|
|
72
82
|
case "openai-codex":
|
|
73
|
-
return (0, openai_codex_1.createOpenAICodexProviderRuntime)(model);
|
|
83
|
+
return (0, openai_codex_1.createOpenAICodexProviderRuntime)(model, config);
|
|
74
84
|
case "github-copilot":
|
|
75
|
-
return (0, github_copilot_1.createGithubCopilotProviderRuntime)(model);
|
|
85
|
+
return (0, github_copilot_1.createGithubCopilotProviderRuntime)(model, config);
|
|
76
86
|
/* v8 ignore next 2 -- exhaustive: all providers handled above @preserve */
|
|
77
87
|
default:
|
|
78
88
|
throw new Error(`unsupported provider for ping: ${provider}`);
|
|
@@ -72,8 +72,7 @@ function getAnthropicReauthGuidance(reason) {
|
|
|
72
72
|
getAnthropicSetupTokenInstructions(),
|
|
73
73
|
].join("\n");
|
|
74
74
|
}
|
|
75
|
-
function resolveAnthropicSetupTokenCredential() {
|
|
76
|
-
const anthropicConfig = (0, config_1.getAnthropicConfig)();
|
|
75
|
+
function resolveAnthropicSetupTokenCredential(anthropicConfig = (0, config_1.getAnthropicConfig)()) {
|
|
77
76
|
const token = anthropicConfig.setupToken?.trim();
|
|
78
77
|
if (!token) {
|
|
79
78
|
throw new Error(getAnthropicReauthGuidance("Anthropic provider is selected but no setup-token credential was found."));
|
|
@@ -395,14 +394,13 @@ async function streamAnthropicMessages(client, model, request) {
|
|
|
395
394
|
settleStreamed: answerStreamer.streamed,
|
|
396
395
|
};
|
|
397
396
|
}
|
|
398
|
-
function createAnthropicProviderRuntime(model) {
|
|
397
|
+
function createAnthropicProviderRuntime(model, anthropicConfig = (0, config_1.getAnthropicConfig)()) {
|
|
399
398
|
(0, runtime_1.emitNervesEvent)({
|
|
400
399
|
component: "engine",
|
|
401
400
|
event: "engine.provider_init",
|
|
402
401
|
message: "anthropic provider init",
|
|
403
402
|
meta: { provider: "anthropic" },
|
|
404
403
|
});
|
|
405
|
-
const anthropicConfig = (0, config_1.getAnthropicConfig)();
|
|
406
404
|
if (!anthropicConfig.setupToken) {
|
|
407
405
|
throw new Error(getAnthropicReauthGuidance("provider 'anthropic' is selected in agent.json but providers.anthropic.setupToken is missing in secrets.json."));
|
|
408
406
|
}
|
|
@@ -410,7 +408,7 @@ function createAnthropicProviderRuntime(model) {
|
|
|
410
408
|
const capabilities = new Set();
|
|
411
409
|
if (modelCaps.reasoningEffort)
|
|
412
410
|
capabilities.add("reasoning-effort");
|
|
413
|
-
const credential = resolveAnthropicSetupTokenCredential();
|
|
411
|
+
const credential = resolveAnthropicSetupTokenCredential(anthropicConfig);
|
|
414
412
|
const refreshToken = anthropicConfig.refreshToken;
|
|
415
413
|
const expiresAt = anthropicConfig.expiresAt;
|
|
416
414
|
function createClient(token) {
|
|
@@ -74,8 +74,7 @@ function createAzureTokenProvider(managedIdentityClientId) {
|
|
|
74
74
|
}
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
|
-
function createAzureProviderRuntime(model) {
|
|
78
|
-
const azureConfig = (0, config_1.getAzureConfig)();
|
|
77
|
+
function createAzureProviderRuntime(model, azureConfig = (0, config_1.getAzureConfig)()) {
|
|
79
78
|
const useApiKey = !!azureConfig.apiKey;
|
|
80
79
|
const authMethod = useApiKey ? "key" : "managed-identity";
|
|
81
80
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -14,14 +14,13 @@ const error_classification_1 = require("./error-classification");
|
|
|
14
14
|
function classifyGithubCopilotError(error) {
|
|
15
15
|
return (0, error_classification_1.classifyHttpError)(error);
|
|
16
16
|
}
|
|
17
|
-
function createGithubCopilotProviderRuntime(model) {
|
|
17
|
+
function createGithubCopilotProviderRuntime(model, config = (0, config_1.getGithubCopilotConfig)()) {
|
|
18
18
|
(0, runtime_1.emitNervesEvent)({
|
|
19
19
|
component: "engine",
|
|
20
20
|
event: "engine.provider_init",
|
|
21
21
|
message: "github-copilot provider init",
|
|
22
22
|
meta: { provider: "github-copilot" },
|
|
23
23
|
});
|
|
24
|
-
const config = (0, config_1.getGithubCopilotConfig)();
|
|
25
24
|
if (!config.githubToken) {
|
|
26
25
|
throw new Error("provider 'github-copilot' is selected in agent.json but providers.github-copilot.githubToken is missing in secrets.json.");
|
|
27
26
|
}
|
|
@@ -21,14 +21,13 @@ function classifyMinimaxError(error) {
|
|
|
21
21
|
}
|
|
22
22
|
const streaming_1 = require("../streaming");
|
|
23
23
|
const model_capabilities_1 = require("../model-capabilities");
|
|
24
|
-
function createMinimaxProviderRuntime(model) {
|
|
24
|
+
function createMinimaxProviderRuntime(model, minimaxConfig = (0, config_1.getMinimaxConfig)()) {
|
|
25
25
|
(0, runtime_1.emitNervesEvent)({
|
|
26
26
|
component: "engine",
|
|
27
27
|
event: "engine.provider_init",
|
|
28
28
|
message: "minimax provider init",
|
|
29
29
|
meta: { provider: "minimax" },
|
|
30
30
|
});
|
|
31
|
-
const minimaxConfig = (0, config_1.getMinimaxConfig)();
|
|
32
31
|
if (!minimaxConfig.apiKey) {
|
|
33
32
|
throw new Error("provider 'minimax' is selected in agent.json but providers.minimax.apiKey is missing in secrets.json.");
|
|
34
33
|
}
|
|
@@ -87,14 +87,13 @@ function getChatGPTAccountIdFromToken(token) {
|
|
|
87
87
|
return "";
|
|
88
88
|
return accountId.trim();
|
|
89
89
|
}
|
|
90
|
-
function createOpenAICodexProviderRuntime(model) {
|
|
90
|
+
function createOpenAICodexProviderRuntime(model, codexConfig = (0, config_1.getOpenAICodexConfig)()) {
|
|
91
91
|
(0, runtime_1.emitNervesEvent)({
|
|
92
92
|
component: "engine",
|
|
93
93
|
event: "engine.provider_init",
|
|
94
94
|
message: "openai-codex provider init",
|
|
95
95
|
meta: { provider: "openai-codex" },
|
|
96
96
|
});
|
|
97
|
-
const codexConfig = (0, config_1.getOpenAICodexConfig)();
|
|
98
97
|
if (!codexConfig.oauthAccessToken) {
|
|
99
98
|
throw new Error(getOpenAICodexReauthGuidance("provider 'openai-codex' is selected in agent.json but providers.openai-codex.oauthAccessToken is missing in secrets.json."));
|
|
100
99
|
}
|
package/dist/mind/prompt.js
CHANGED
|
@@ -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
|
-
|
|
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();
|