@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.
- package/README.md +2 -2
- package/RepairGuide.ouro/psyche/IDENTITY.md +1 -1
- package/RepairGuide.ouro/psyche/SOUL.md +1 -1
- package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +4 -4
- package/changelog.json +36 -43
- package/dist/heart/auth/auth-flow.js +3 -3
- package/dist/heart/core.js +0 -12
- package/dist/heart/daemon/agent-config-check.js +75 -168
- package/dist/heart/daemon/agentic-repair.js +2 -9
- package/dist/heart/daemon/cli-exec.js +67 -230
- package/dist/heart/daemon/cli-help.js +1 -1
- package/dist/heart/daemon/cli-render.js +2 -14
- package/dist/heart/daemon/daemon-entry.js +0 -30
- package/dist/heart/daemon/daemon-health.js +0 -7
- package/dist/heart/daemon/daemon-rollup.js +1 -2
- package/dist/heart/daemon/doctor.js +0 -18
- package/dist/heart/daemon/inner-status.js +0 -13
- package/dist/heart/hatch/hatch-flow.js +0 -20
- package/dist/heart/provider-binding-resolver.js +109 -97
- package/dist/heart/provider-credentials.js +2 -2
- package/dist/heart/provider-failover.js +1 -1
- package/dist/heart/provider-readiness-cache.js +40 -0
- package/dist/heart/provider-visibility.js +2 -2
- package/dist/heart/start-of-turn-packet.js +1 -1
- package/dist/nerves/coverage/file-completeness.js +0 -10
- package/dist/senses/pipeline.js +18 -37
- package/package.json +2 -2
- package/RepairGuide.ouro/skills/diagnose-bootstrap-drift.md +0 -54
- package/dist/heart/daemon/drift-detection.js +0 -146
- package/dist/heart/provider-state.js +0 -216
|
@@ -108,15 +108,6 @@ const SENSITIVE_CONFIG_KEYS = ["apiKey", "token", "secret", "password"];
|
|
|
108
108
|
function credentialKeyLeaks(raw) {
|
|
109
109
|
return SENSITIVE_CONFIG_KEYS.filter((key) => raw.includes(`"${key}"`));
|
|
110
110
|
}
|
|
111
|
-
function checkCredentialLeak(checks, label, raw) {
|
|
112
|
-
const found = credentialKeyLeaks(raw);
|
|
113
|
-
if (found.length > 0) {
|
|
114
|
-
checks.push({ label, status: "warn", detail: `contains credential-looking keys: ${found.join(", ")}` });
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
checks.push({ label, status: "pass", detail: "no credential keys" });
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
111
|
function checkAgents(deps) {
|
|
121
112
|
const checks = [];
|
|
122
113
|
if (!deps.existsSync(deps.bundlesRoot)) {
|
|
@@ -368,15 +359,6 @@ function checkSecurity(deps) {
|
|
|
368
359
|
checks.push({ label: `${agentDir} credential leak`, status: "fail", detail: "could not read agent.json" });
|
|
369
360
|
}
|
|
370
361
|
}
|
|
371
|
-
const providerStatePath = `${deps.bundlesRoot}/${agentDir}/state/providers.json`;
|
|
372
|
-
if (deps.existsSync(providerStatePath)) {
|
|
373
|
-
try {
|
|
374
|
-
checkCredentialLeak(checks, `${agentDir} state/providers.json credential leak`, deps.readFileSync(providerStatePath));
|
|
375
|
-
}
|
|
376
|
-
catch {
|
|
377
|
-
checks.push({ label: `${agentDir} state/providers.json credential leak`, status: "fail", detail: "could not read state/providers.json" });
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
362
|
}
|
|
381
363
|
if (checks.length === 0) {
|
|
382
364
|
checks.push({ label: "security", status: "warn", detail: "no agents found" });
|
|
@@ -74,18 +74,6 @@ function buildInnerStatusOutput(input) {
|
|
|
74
74
|
// Attention
|
|
75
75
|
const thoughtWord = attentionCount === 1 ? "thought" : "thoughts";
|
|
76
76
|
lines.push(` attention: ${attentionCount} held ${thoughtWord}`);
|
|
77
|
-
// Layer 4 drift advisory. Renders one line per finding with the
|
|
78
|
-
// lane, intent vs observed binding, and the copy-pasteable repair
|
|
79
|
-
// command. Suppressed entirely when no findings exist (or the field
|
|
80
|
-
// is absent — pre-Layer-4 callers).
|
|
81
|
-
const driftFindings = input.driftFindings ?? [];
|
|
82
|
-
if (driftFindings.length > 0) {
|
|
83
|
-
lines.push(" drift advisory:");
|
|
84
|
-
for (const finding of driftFindings) {
|
|
85
|
-
lines.push(` - ${finding.lane}: intent ${finding.intentProvider}/${finding.intentModel} vs observed ${finding.observedProvider}/${finding.observedModel}`);
|
|
86
|
-
lines.push(` repair: ${finding.repairCommand}`);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
77
|
(0, runtime_1.emitNervesEvent)({
|
|
90
78
|
component: "daemon",
|
|
91
79
|
event: "daemon.inner_status_read",
|
|
@@ -95,7 +83,6 @@ function buildInnerStatusOutput(input) {
|
|
|
95
83
|
status: runtimeState?.status ?? "unknown",
|
|
96
84
|
journalCount: journalFiles.length,
|
|
97
85
|
attentionCount,
|
|
98
|
-
driftCount: driftFindings.length,
|
|
99
86
|
},
|
|
100
87
|
});
|
|
101
88
|
return lines.join("\n");
|
|
@@ -44,9 +44,6 @@ const runtime_1 = require("../../nerves/runtime");
|
|
|
44
44
|
const auth_flow_1 = require("../auth/auth-flow");
|
|
45
45
|
const provider_models_1 = require("../provider-models");
|
|
46
46
|
const habit_parser_1 = require("../habits/habit-parser");
|
|
47
|
-
const machine_identity_1 = require("../machine-identity");
|
|
48
|
-
const provider_credentials_1 = require("../provider-credentials");
|
|
49
|
-
const provider_state_1 = require("../provider-state");
|
|
50
47
|
const hatch_specialist_1 = require("./hatch-specialist");
|
|
51
48
|
function requiredCredentialKeys(provider) {
|
|
52
49
|
return identity_1.PROVIDER_CREDENTIALS[provider].required;
|
|
@@ -134,22 +131,6 @@ function writeHatchlingAgentConfig(bundleRoot, input) {
|
|
|
134
131
|
template.enabled = true;
|
|
135
132
|
fs.writeFileSync(path.join(bundleRoot, "agent.json"), `${JSON.stringify(template, null, 2)}\n`, "utf-8");
|
|
136
133
|
}
|
|
137
|
-
function writeHatchlingProviderState(bundleRoot, input, now) {
|
|
138
|
-
const model = (0, provider_models_1.getDefaultModelForProvider)(input.provider);
|
|
139
|
-
const machine = (0, machine_identity_1.loadOrCreateMachineIdentity)({
|
|
140
|
-
homeDir: (0, provider_credentials_1.providerCredentialMachineHomeDir)(),
|
|
141
|
-
now: () => now,
|
|
142
|
-
});
|
|
143
|
-
const state = (0, provider_state_1.bootstrapProviderStateFromAgentConfig)({
|
|
144
|
-
machineId: machine.machineId,
|
|
145
|
-
now,
|
|
146
|
-
agentConfig: {
|
|
147
|
-
humanFacing: { provider: input.provider, model },
|
|
148
|
-
agentFacing: { provider: input.provider, model },
|
|
149
|
-
},
|
|
150
|
-
});
|
|
151
|
-
(0, provider_state_1.writeProviderState)(bundleRoot, state);
|
|
152
|
-
}
|
|
153
134
|
async function runHatchFlow(input, deps = {}) {
|
|
154
135
|
(0, runtime_1.emitNervesEvent)({
|
|
155
136
|
component: "daemon",
|
|
@@ -186,7 +167,6 @@ async function runHatchFlow(input, deps = {}) {
|
|
|
186
167
|
writeReadme(path.join(bundleRoot, "senses", "teams"), "Teams sense config.");
|
|
187
168
|
writeHatchlingAgentConfig(bundleRoot, input);
|
|
188
169
|
const credentialPath = await storeHatchlingProviderCredentials(input.agentName, input.provider, input.credentials);
|
|
189
|
-
writeHatchlingProviderState(bundleRoot, input, now);
|
|
190
170
|
writeDiaryScaffold(bundleRoot);
|
|
191
171
|
writeFriendImprint(bundleRoot, input.humanName, now);
|
|
192
172
|
writeHeartbeatHabit(bundleRoot, now);
|
|
@@ -1,16 +1,55 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.normalizeProviderLane = normalizeProviderLane;
|
|
4
37
|
exports.resolveEffectiveProviderBinding = resolveEffectiveProviderBinding;
|
|
38
|
+
const path = __importStar(require("path"));
|
|
5
39
|
const runtime_1 = require("../nerves/runtime");
|
|
40
|
+
const identity_1 = require("./identity");
|
|
41
|
+
const auth_flow_1 = require("./auth/auth-flow");
|
|
6
42
|
const provider_credentials_1 = require("./provider-credentials");
|
|
7
|
-
const
|
|
43
|
+
const provider_readiness_cache_1 = require("./provider-readiness-cache");
|
|
8
44
|
function legacyLaneWarning(selector, lane) {
|
|
9
45
|
return {
|
|
10
46
|
code: "legacy-lane-selector",
|
|
11
47
|
message: `${selector} is legacy provider wording; using ${lane} lane`,
|
|
12
48
|
};
|
|
13
49
|
}
|
|
50
|
+
function facingKeyForProviderLane(lane) {
|
|
51
|
+
return lane === "outward" ? "humanFacing" : "agentFacing";
|
|
52
|
+
}
|
|
14
53
|
function normalizeProviderLane(selector) {
|
|
15
54
|
switch (selector) {
|
|
16
55
|
case "outward":
|
|
@@ -25,12 +64,10 @@ function normalizeProviderLane(selector) {
|
|
|
25
64
|
return { lane: "inner", warnings: [legacyLaneWarning(selector, "inner")] };
|
|
26
65
|
}
|
|
27
66
|
}
|
|
28
|
-
function buildUseRepair(agentName, lane
|
|
67
|
+
function buildUseRepair(agentName, lane) {
|
|
29
68
|
return {
|
|
30
|
-
command: `ouro use --agent ${agentName} --lane ${lane} --provider <provider> --model <model
|
|
31
|
-
message:
|
|
32
|
-
? `Rewrite this machine's ${lane} provider binding for ${agentName}.`
|
|
33
|
-
: `Choose the provider/model this machine should use for ${agentName}'s ${lane} lane.`,
|
|
69
|
+
command: `ouro use --agent ${agentName} --lane ${lane} --provider <provider> --model <model>`,
|
|
70
|
+
message: `Choose the provider/model ${agentName}'s ${lane} lane should use in agent.json.`,
|
|
34
71
|
};
|
|
35
72
|
}
|
|
36
73
|
function buildAuthRepair(agentName, provider) {
|
|
@@ -39,16 +76,10 @@ function buildAuthRepair(agentName, provider) {
|
|
|
39
76
|
message: `Store ${provider} credentials in ${agentName}'s vault.`,
|
|
40
77
|
};
|
|
41
78
|
}
|
|
42
|
-
function
|
|
43
|
-
return {
|
|
44
|
-
code: "provider-state-missing",
|
|
45
|
-
message: `No local provider binding exists for ${agentName} on this machine.`,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
function invalidProviderStateWarning(agentName) {
|
|
79
|
+
function invalidAgentConfigWarning(agentName, error) {
|
|
49
80
|
return {
|
|
50
|
-
code: "
|
|
51
|
-
message: `
|
|
81
|
+
code: "agent-config-invalid",
|
|
82
|
+
message: `agent.json provider selection for ${agentName} is invalid: ${error}`,
|
|
52
83
|
};
|
|
53
84
|
}
|
|
54
85
|
function missingCredentialWarning(provider) {
|
|
@@ -121,120 +152,101 @@ function resolveCredential(poolResult, provider, agentName) {
|
|
|
121
152
|
warnings: [missingCredentialWarning(provider)],
|
|
122
153
|
};
|
|
123
154
|
}
|
|
124
|
-
function
|
|
125
|
-
|
|
126
|
-
status:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
155
|
+
function resolveReadiness(agentName, lane, provider, model, credential) {
|
|
156
|
+
if (credential.status === "missing") {
|
|
157
|
+
return { status: "unknown", reason: "credential-missing" };
|
|
158
|
+
}
|
|
159
|
+
if (credential.status === "invalid-pool") {
|
|
160
|
+
return { status: "unknown", reason: "credential-pool-invalid" };
|
|
161
|
+
}
|
|
162
|
+
if (credential.status === "present") {
|
|
163
|
+
const cached = (0, provider_readiness_cache_1.readProviderLaneReadiness)({
|
|
164
|
+
agentName,
|
|
165
|
+
lane,
|
|
166
|
+
provider,
|
|
167
|
+
model,
|
|
168
|
+
credentialRevision: credential.revision,
|
|
169
|
+
});
|
|
170
|
+
if (cached) {
|
|
171
|
+
return {
|
|
172
|
+
status: cached.status,
|
|
173
|
+
checkedAt: cached.checkedAt,
|
|
174
|
+
...(cached.error ? { error: cached.error } : {}),
|
|
175
|
+
...(cached.attempts !== undefined ? { attempts: cached.attempts } : {}),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return { status: "unknown" };
|
|
132
180
|
}
|
|
133
|
-
function
|
|
134
|
-
return
|
|
135
|
-
...readinessFromState(readiness),
|
|
136
|
-
status: "stale",
|
|
137
|
-
previousStatus: readiness.status,
|
|
138
|
-
reason,
|
|
139
|
-
};
|
|
181
|
+
function isAgentProvider(value) {
|
|
182
|
+
return typeof value === "string" && Object.prototype.hasOwnProperty.call(identity_1.PROVIDER_CREDENTIALS, value);
|
|
140
183
|
}
|
|
141
|
-
function
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
184
|
+
function resolveAgentConfigLane(input, lane) {
|
|
185
|
+
const configPath = path.join(input.agentRoot, "agent.json");
|
|
186
|
+
try {
|
|
187
|
+
const { config, configPath: resolvedConfigPath } = (0, auth_flow_1.readAgentConfigForAgent)(input.agentName, path.dirname(input.agentRoot));
|
|
188
|
+
const facingKey = facingKeyForProviderLane(lane);
|
|
189
|
+
const binding = config[facingKey];
|
|
190
|
+
/* v8 ignore next -- readAgentConfigForAgent rejects unsupported providers before this defensive guard @preserve */
|
|
191
|
+
if (!isAgentProvider(binding.provider)) {
|
|
192
|
+
return { ok: false, configPath: resolvedConfigPath, error: `${facingKey}.provider must be a supported provider` };
|
|
148
193
|
}
|
|
149
|
-
|
|
150
|
-
|
|
194
|
+
const model = typeof binding.model === "string" ? binding.model.trim() : "";
|
|
195
|
+
if (model.length === 0) {
|
|
196
|
+
return { ok: false, configPath: resolvedConfigPath, error: `${facingKey}.model must be a non-empty string` };
|
|
151
197
|
}
|
|
152
|
-
return { readiness: { status: "unknown" }, warnings: [] };
|
|
153
|
-
}
|
|
154
|
-
if (input.readiness.provider !== input.provider || input.readiness.model !== input.model) {
|
|
155
198
|
return {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}],
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
if (input.credential.status === "present"
|
|
164
|
-
&& input.readiness.credentialRevision !== undefined
|
|
165
|
-
&& input.readiness.credentialRevision !== input.credential.revision) {
|
|
166
|
-
return {
|
|
167
|
-
readiness: staleReadiness(input.readiness, "credential-revision-changed"),
|
|
168
|
-
warnings: [{
|
|
169
|
-
code: "readiness-stale",
|
|
170
|
-
message: `${input.provider}/${input.model} readiness is stale because credential revision changed from ${input.readiness.credentialRevision} to ${input.credential.revision}.`,
|
|
171
|
-
}],
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
if (input.credential.status === "missing") {
|
|
175
|
-
return {
|
|
176
|
-
readiness: staleReadiness(input.readiness, "credential-missing"),
|
|
177
|
-
warnings: [],
|
|
199
|
+
ok: true,
|
|
200
|
+
configPath: resolvedConfigPath,
|
|
201
|
+
provider: binding.provider,
|
|
202
|
+
model,
|
|
178
203
|
};
|
|
179
204
|
}
|
|
180
|
-
|
|
205
|
+
catch (error) {
|
|
181
206
|
return {
|
|
182
|
-
|
|
183
|
-
|
|
207
|
+
ok: false,
|
|
208
|
+
configPath,
|
|
209
|
+
/* v8 ignore next -- readAgentConfigForAgent/file IO failures are Error instances in supported runtimes @preserve */
|
|
210
|
+
error: error instanceof Error ? error.message : String(error),
|
|
184
211
|
};
|
|
185
212
|
}
|
|
186
|
-
if (input.credential.status === "not-loaded") {
|
|
187
|
-
return { readiness: readinessFromState(input.readiness), warnings: [] };
|
|
188
|
-
}
|
|
189
|
-
return { readiness: readinessFromState(input.readiness), warnings: [] };
|
|
190
213
|
}
|
|
191
214
|
function resolveEffectiveProviderBinding(input) {
|
|
192
215
|
const laneResolution = normalizeProviderLane(input.lane);
|
|
193
|
-
const
|
|
194
|
-
if (!
|
|
195
|
-
const reason = stateResult.reason === "missing" ? "provider-state-missing" : "provider-state-invalid";
|
|
196
|
-
const stateWarning = stateResult.reason === "missing"
|
|
197
|
-
? missingProviderStateWarning(input.agentName)
|
|
198
|
-
: invalidProviderStateWarning(input.agentName);
|
|
216
|
+
const agentConfigResult = resolveAgentConfigLane(input, laneResolution.lane);
|
|
217
|
+
if (!agentConfigResult.ok) {
|
|
199
218
|
const result = {
|
|
200
219
|
ok: false,
|
|
201
220
|
lane: laneResolution.lane,
|
|
202
|
-
reason,
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
221
|
+
reason: "agent-config-invalid",
|
|
222
|
+
configPath: agentConfigResult.configPath,
|
|
223
|
+
error: agentConfigResult.error,
|
|
224
|
+
warnings: [...laneResolution.warnings, invalidAgentConfigWarning(input.agentName, agentConfigResult.error)],
|
|
225
|
+
repair: buildUseRepair(input.agentName, laneResolution.lane),
|
|
206
226
|
};
|
|
207
227
|
(0, runtime_1.emitNervesEvent)({
|
|
208
228
|
component: "config/identity",
|
|
209
229
|
event: "config.provider_binding_resolution_failed",
|
|
210
230
|
message: "provider binding resolution failed",
|
|
211
|
-
meta: { agentName: input.agentName, lane: laneResolution.lane, reason },
|
|
231
|
+
meta: { agentName: input.agentName, lane: laneResolution.lane, reason: result.reason },
|
|
212
232
|
});
|
|
213
233
|
return result;
|
|
214
234
|
}
|
|
215
|
-
const laneBinding = stateResult.state.lanes[laneResolution.lane];
|
|
216
235
|
const poolResult = (0, provider_credentials_1.readProviderCredentialPool)(input.agentName);
|
|
217
|
-
const credentialResult = resolveCredential(poolResult,
|
|
218
|
-
const
|
|
219
|
-
provider: laneBinding.provider,
|
|
220
|
-
model: laneBinding.model,
|
|
221
|
-
readiness: stateResult.state.readiness[laneResolution.lane],
|
|
222
|
-
credential: credentialResult.credential,
|
|
223
|
-
});
|
|
236
|
+
const credentialResult = resolveCredential(poolResult, agentConfigResult.provider, input.agentName);
|
|
237
|
+
const readiness = resolveReadiness(input.agentName, laneResolution.lane, agentConfigResult.provider, agentConfigResult.model, credentialResult.credential);
|
|
224
238
|
const warnings = [
|
|
225
239
|
...laneResolution.warnings,
|
|
226
240
|
...credentialResult.warnings,
|
|
227
|
-
...readinessResult.warnings,
|
|
228
241
|
];
|
|
229
242
|
const binding = {
|
|
230
243
|
lane: laneResolution.lane,
|
|
231
|
-
provider:
|
|
232
|
-
model:
|
|
233
|
-
source:
|
|
234
|
-
|
|
235
|
-
statePath: stateResult.statePath,
|
|
244
|
+
provider: agentConfigResult.provider,
|
|
245
|
+
model: agentConfigResult.model,
|
|
246
|
+
source: "agent.json",
|
|
247
|
+
configPath: agentConfigResult.configPath,
|
|
236
248
|
credential: credentialResult.credential,
|
|
237
|
-
readiness
|
|
249
|
+
readiness,
|
|
238
250
|
warnings,
|
|
239
251
|
};
|
|
240
252
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -372,13 +372,13 @@ async function upsertProviderCredential(input) {
|
|
|
372
372
|
password: JSON.stringify(payload),
|
|
373
373
|
notes: "Ouro provider credentials. The vault item password is a versioned JSON payload.",
|
|
374
374
|
});
|
|
375
|
-
input.onProgress?.(`refreshing
|
|
375
|
+
input.onProgress?.(`refreshing in-memory provider credential pool from ${input.agentName}'s vault...`);
|
|
376
376
|
const refreshResult = await refreshProviderCredentialPool(input.agentName, {
|
|
377
377
|
providers: [input.provider],
|
|
378
378
|
onProgress: input.onProgress,
|
|
379
379
|
});
|
|
380
380
|
if (!refreshResult.ok) {
|
|
381
|
-
throw new Error(`credential stored in vault, but the
|
|
381
|
+
throw new Error(`credential stored in vault, but the in-memory provider credential pool could not be refreshed: ${refreshResult.error}. ` +
|
|
382
382
|
`Run 'ouro provider refresh --agent ${input.agentName}' after fixing vault access, then run 'ouro auth verify --agent ${input.agentName}'.`);
|
|
383
383
|
}
|
|
384
384
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -268,7 +268,7 @@ async function runMachineProviderFailoverInventory(agentName, currentProvider, o
|
|
|
268
268
|
}
|
|
269
269
|
/**
|
|
270
270
|
* Re-verify a failover candidate is actually reachable right before we mutate
|
|
271
|
-
*
|
|
271
|
+
* agent.json. The inventory ping that produced the candidate may be stale
|
|
272
272
|
* (creds revoked between inventory and reply); without this preflight, an
|
|
273
273
|
* agent-driven "switch to <provider>" can move the lane onto an unreachable
|
|
274
274
|
* provider and brick the next turn.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.recordProviderLaneReadiness = recordProviderLaneReadiness;
|
|
4
|
+
exports.readProviderLaneReadiness = readProviderLaneReadiness;
|
|
5
|
+
exports.clearProviderReadinessCache = clearProviderReadinessCache;
|
|
6
|
+
const runtime_1 = require("../nerves/runtime");
|
|
7
|
+
const readinessByLane = new Map();
|
|
8
|
+
function cacheKey(agentName, lane) {
|
|
9
|
+
return `${agentName}\0${lane}`;
|
|
10
|
+
}
|
|
11
|
+
function recordProviderLaneReadiness(entry) {
|
|
12
|
+
readinessByLane.set(cacheKey(entry.agentName, entry.lane), { ...entry });
|
|
13
|
+
(0, runtime_1.emitNervesEvent)({
|
|
14
|
+
component: "config/identity",
|
|
15
|
+
event: "config.provider_readiness_recorded",
|
|
16
|
+
message: "recorded in-memory provider readiness",
|
|
17
|
+
meta: {
|
|
18
|
+
agentName: entry.agentName,
|
|
19
|
+
lane: entry.lane,
|
|
20
|
+
provider: entry.provider,
|
|
21
|
+
model: entry.model,
|
|
22
|
+
status: entry.status,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function readProviderLaneReadiness(input) {
|
|
27
|
+
const entry = readinessByLane.get(cacheKey(input.agentName, input.lane));
|
|
28
|
+
if (!entry)
|
|
29
|
+
return null;
|
|
30
|
+
if (entry.provider !== input.provider)
|
|
31
|
+
return null;
|
|
32
|
+
if (entry.model !== input.model)
|
|
33
|
+
return null;
|
|
34
|
+
if (entry.credentialRevision !== input.credentialRevision)
|
|
35
|
+
return null;
|
|
36
|
+
return { ...entry };
|
|
37
|
+
}
|
|
38
|
+
function clearProviderReadinessCache() {
|
|
39
|
+
readinessByLane.clear();
|
|
40
|
+
}
|
|
@@ -128,12 +128,12 @@ function formatProviderVisibilityLine(lane) {
|
|
|
128
128
|
function formatAgentProviderVisibilityForPrompt(visibility) {
|
|
129
129
|
if (visibility.lanes.every((lane) => lane.status === "unconfigured")) {
|
|
130
130
|
return [
|
|
131
|
-
"provider bindings are not configured
|
|
131
|
+
"provider bindings are not configured in agent.json.",
|
|
132
132
|
...visibility.lanes.map((lane) => `- ${formatProviderVisibilityLine(lane)}`),
|
|
133
133
|
].join("\n");
|
|
134
134
|
}
|
|
135
135
|
return [
|
|
136
|
-
"runtime uses
|
|
136
|
+
"runtime uses provider bindings from agent.json:",
|
|
137
137
|
...visibility.lanes.map((lane) => `- ${formatProviderVisibilityLine(lane)}`),
|
|
138
138
|
].join("\n");
|
|
139
139
|
}
|
|
@@ -228,7 +228,7 @@ function renderStartOfTurnPacket(packet) {
|
|
|
228
228
|
// are actionable "fix your git" signals. bundleState is preferred
|
|
229
229
|
// because it's structured (array of enum values) while syncFailure is
|
|
230
230
|
// a legacy free-form string; both render when populated.
|
|
231
|
-
{ label: "provider", content: packet.
|
|
231
|
+
{ label: "provider", content: packet.providerSelection ?? "", priority: 8 },
|
|
232
232
|
{ label: "bundleState", content: (0, bundle_state_1.renderBundleStateHint)(packet.bundleState ?? []), priority: 7 },
|
|
233
233
|
{ label: "syncFailure", content: packet.syncFailure ?? "", priority: 7 },
|
|
234
234
|
{ label: "resume", content: packet.resumeHint, priority: 6 },
|
|
@@ -93,16 +93,6 @@ const DISPATCH_EXEMPT_PATTERNS = [
|
|
|
93
93
|
// buildDaemonHealthState → DaemonHealthWriter) owns observability via
|
|
94
94
|
// daemon.health_written when the rolled-up state is persisted.
|
|
95
95
|
"daemon/daemon-rollup",
|
|
96
|
-
// Drift comparator + thin I/O loader: `detectProviderBindingDrift`
|
|
97
|
-
// is a pure intent-vs-observed comparator with no side effects;
|
|
98
|
-
// `loadDriftInputsForAgent` is a small fs-read wrapper that returns
|
|
99
|
-
// `null` on missing/invalid state rather than emitting. The caller
|
|
100
|
-
// (daemon-entry.ts buildDaemonHealthState's per-agent drift probe)
|
|
101
|
-
// owns observability — drift findings ride along through
|
|
102
|
-
// `daemon.health_written` as part of the rolled-up state, and
|
|
103
|
-
// `agent-config-check.ts` carries `driftFindings` through its
|
|
104
|
-
// existing instrumentation. Same pattern as `daemon-rollup`.
|
|
105
|
-
"daemon/drift-detection",
|
|
106
96
|
// Attachment helper modules: generic file-path/extension utilities and the
|
|
107
97
|
// source registry are pure support seams. The orchestrator/adapters that
|
|
108
98
|
// call them own the observability.
|
package/dist/senses/pipeline.js
CHANGED
|
@@ -47,12 +47,12 @@ const commands_1 = require("./commands");
|
|
|
47
47
|
const continuity_1 = require("./continuity");
|
|
48
48
|
const manager_1 = require("../heart/bridges/manager");
|
|
49
49
|
const identity_1 = require("../heart/identity");
|
|
50
|
+
const auth_flow_1 = require("../heart/auth/auth-flow");
|
|
50
51
|
const socket_client_1 = require("../heart/daemon/socket-client");
|
|
51
52
|
const active_work_1 = require("../heart/active-work");
|
|
52
53
|
const delegation_1 = require("../heart/delegation");
|
|
53
54
|
const obligations_1 = require("../arc/obligations");
|
|
54
55
|
const provider_failover_1 = require("../heart/provider-failover");
|
|
55
|
-
const provider_state_1 = require("../heart/provider-state");
|
|
56
56
|
const tempo_1 = require("../heart/tempo");
|
|
57
57
|
const temporal_view_1 = require("../heart/temporal-view");
|
|
58
58
|
const start_of_turn_packet_1 = require("../heart/start-of-turn-packet");
|
|
@@ -88,17 +88,13 @@ function providerLaneForChannel(channel) {
|
|
|
88
88
|
return channel === "inner" ? "inner" : "outward";
|
|
89
89
|
}
|
|
90
90
|
function resolveCurrentFailoverBinding(agentName, lane) {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
const binding = stateResult.state.lanes[lane];
|
|
94
|
-
return { provider: binding.provider, model: binding.model };
|
|
95
|
-
}
|
|
96
|
-
const agentConfig = (0, identity_1.loadAgentConfig)();
|
|
91
|
+
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
92
|
+
const { config: agentConfig } = (0, auth_flow_1.readAgentConfigForAgent)(agentName, path.dirname(agentRoot));
|
|
97
93
|
const fallback = lane === "inner" ? agentConfig.agentFacing : agentConfig.humanFacing;
|
|
98
94
|
return { provider: fallback.provider, model: fallback.model };
|
|
99
95
|
}
|
|
100
96
|
/**
|
|
101
|
-
* Apply an agent-driven failover switch to
|
|
97
|
+
* Apply an agent-driven failover switch to agent.json, but only after
|
|
102
98
|
* re-pinging the candidate. The inventory ping that produced the candidate
|
|
103
99
|
* may be stale by the time the agent replies — without this preflight, a
|
|
104
100
|
* "switch to <provider>" reply can move the lane onto an unreachable provider.
|
|
@@ -109,38 +105,23 @@ function resolveCurrentFailoverBinding(agentName, lane) {
|
|
|
109
105
|
* surface the refusal to the agent
|
|
110
106
|
* Throws on disk errors only (caught by caller as before).
|
|
111
107
|
*/
|
|
112
|
-
async function
|
|
108
|
+
async function writeFailoverAgentConfigSwitch(agentName, action) {
|
|
113
109
|
const validation = await (0, provider_failover_1.validateFailoverSwitchCandidate)(agentName, { provider: action.provider, model: action.model });
|
|
114
110
|
if (!validation.ok) {
|
|
115
111
|
return { ok: false, refused: true, classification: validation.classification, message: validation.message };
|
|
116
112
|
}
|
|
117
|
-
const agentRoot = (0, identity_1.getAgentRoot)(
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
source: "local",
|
|
128
|
-
updatedAt,
|
|
129
|
-
};
|
|
130
|
-
const readiness = { ...stateResult.state.readiness };
|
|
131
|
-
readiness[action.lane] = {
|
|
132
|
-
status: "ready",
|
|
133
|
-
provider: action.provider,
|
|
134
|
-
model: action.model,
|
|
135
|
-
checkedAt: updatedAt,
|
|
136
|
-
...(action.credentialRevision ? { credentialRevision: action.credentialRevision } : {}),
|
|
113
|
+
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
114
|
+
const { configPath, config } = (0, auth_flow_1.readAgentConfigForAgent)(agentName, path.dirname(agentRoot));
|
|
115
|
+
const facingKey = action.lane === "inner" ? "agentFacing" : "humanFacing";
|
|
116
|
+
const nextConfig = {
|
|
117
|
+
...config,
|
|
118
|
+
[facingKey]: {
|
|
119
|
+
...config[facingKey],
|
|
120
|
+
provider: action.provider,
|
|
121
|
+
model: action.model,
|
|
122
|
+
},
|
|
137
123
|
};
|
|
138
|
-
(
|
|
139
|
-
...stateResult.state,
|
|
140
|
-
updatedAt,
|
|
141
|
-
lanes,
|
|
142
|
-
readiness,
|
|
143
|
-
});
|
|
124
|
+
fs.writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf-8");
|
|
144
125
|
return { ok: true };
|
|
145
126
|
}
|
|
146
127
|
function formatFailoverSwitchLabel(action) {
|
|
@@ -219,7 +200,7 @@ async function handleInboundTurn(input) {
|
|
|
219
200
|
if (failoverAction.action === "switch") {
|
|
220
201
|
let switchOutcome = null;
|
|
221
202
|
try {
|
|
222
|
-
switchOutcome = await
|
|
203
|
+
switchOutcome = await writeFailoverAgentConfigSwitch(failoverAgentName, failoverAction);
|
|
223
204
|
/* v8 ignore start -- defensive: write failure during provider switch @preserve */
|
|
224
205
|
}
|
|
225
206
|
catch (switchError) {
|
|
@@ -499,7 +480,7 @@ async function handleInboundTurn(input) {
|
|
|
499
480
|
startOfTurnPacket.syncFailure = syncFailure;
|
|
500
481
|
}
|
|
501
482
|
if (ctx.providerVisibility) {
|
|
502
|
-
startOfTurnPacket.
|
|
483
|
+
startOfTurnPacket.providerSelection = (0, provider_visibility_1.formatAgentProviderVisibilityForStartOfTurn)(ctx.providerVisibility);
|
|
503
484
|
}
|
|
504
485
|
// Structured bundle state detection — surfaces discrete issues the
|
|
505
486
|
// agent can remediate via the bundle_* tools. Runs independently of
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ouro.bot/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.555",
|
|
4
4
|
"main": "dist/heart/daemon/ouro-entry.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"cli": "dist/heart/daemon/ouro-bot-entry.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"test:mailbox-ui": "npm test --prefix packages/mailbox-ui",
|
|
35
35
|
"test:coverage:vitest": "vitest run --coverage",
|
|
36
36
|
"test:coverage": "node scripts/run-coverage-gate.cjs",
|
|
37
|
-
"build": "tsc && (cd packages/mailbox-ui && npm install --ignore-scripts 2>/dev/null && npm run build && cd ../.. && node scripts/copy-mailbox-ui.cjs) || echo 'mailbox-ui build skipped'",
|
|
37
|
+
"build": "node scripts/clean-dist.cjs && tsc && (cd packages/mailbox-ui && npm install --ignore-scripts 2>/dev/null && npm run build && cd ../.. && node scripts/copy-mailbox-ui.cjs) || echo 'mailbox-ui build skipped'",
|
|
38
38
|
"lint": "eslint src/",
|
|
39
39
|
"release:preflight": "node scripts/release-preflight.cjs",
|
|
40
40
|
"release:smoke": "node scripts/release-smoke.cjs",
|