@ouro.bot/cli 0.1.0-alpha.353 → 0.1.0-alpha.354
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 +17 -8
- package/changelog.json +7 -0
- package/dist/heart/daemon/cli-render.js +50 -2
- package/dist/heart/daemon/daemon.js +6 -0
- package/dist/heart/daemon/pulse.js +15 -3
- package/dist/heart/outlook/outlook-view.js +1 -0
- package/dist/heart/outlook/readers/agent-machine.js +4 -0
- package/dist/heart/provider-visibility.js +183 -0
- package/dist/heart/start-of-turn-packet.js +4 -0
- package/dist/heart/turn-context.js +4 -0
- package/dist/mind/prompt.js +12 -2
- package/dist/senses/pipeline.js +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,8 @@ Ouroboros is a TypeScript harness for daemon-managed agents that live in externa
|
|
|
11
11
|
- `ouro up` starts the daemon from the installed production version, syncs the launcher, installs workflow helpers, and reconciles stale runtime state.
|
|
12
12
|
- `ouro dev` starts the daemon from a local repo build. It auto-builds from source, disables launchd auto-restart (so the installed daemon doesn't respawn underneath you), persists the repo path in `~/.ouro-cli/dev-config.json` for next time, and force-restarts the daemon. If you run `ouro dev` from inside the repo, it detects the CWD automatically. Run `ouro up` to return to production mode (this also cleans up `dev-config.json`).
|
|
13
13
|
- Agent bundles live outside the repo at `~/AgentBundles/<agent>.ouro/`.
|
|
14
|
-
-
|
|
14
|
+
- Provider credentials live outside the repo at `~/.agentsecrets/providers.json`.
|
|
15
|
+
- Sense-specific secrets live outside the repo at `~/.agentsecrets/<agent>/secrets.json`.
|
|
15
16
|
- Machine-scoped test and runtime spillover lives under `~/.agentstate/...`.
|
|
16
17
|
|
|
17
18
|
Current first-class senses:
|
|
@@ -90,8 +91,10 @@ Task docs do not live in this repo anymore. Planning and doing docs live in the
|
|
|
90
91
|
|
|
91
92
|
## Runtime Truths
|
|
92
93
|
|
|
93
|
-
- `agent.json` is the source of truth for
|
|
94
|
-
- `configPath` must point to `~/.agentsecrets/<agent>/secrets.json
|
|
94
|
+
- `agent.json` is the source of truth for phrase pools, context settings, enabled senses, and the agent's `configPath`. Legacy `humanFacing`/`agentFacing` values are bootstrap inputs, not live machine fallback.
|
|
95
|
+
- `configPath` must point to `~/.agentsecrets/<agent>/secrets.json` for sense-specific secrets.
|
|
96
|
+
- `state/providers.json` is the local source of truth for provider+model selection on this machine. It has two lanes: `outward` for CLI, Teams, and BlueBubbles turns, and `inner` for inner dialogue.
|
|
97
|
+
- `~/.agentsecrets/providers.json` is the machine credential pool. It stores provider credentials only, records which agent contributed them, and is shared by all agents on that machine.
|
|
95
98
|
- The daemon discovers bundles dynamically from `~/AgentBundles`.
|
|
96
99
|
- `ouro status` reports version, last-updated time, discovered agents, senses, and workers.
|
|
97
100
|
- `bundle-meta.json` tracks the runtime version that last touched a bundle.
|
|
@@ -104,17 +107,22 @@ Task docs do not live in this repo anymore. Planning and doing docs live in the
|
|
|
104
107
|
- `running`
|
|
105
108
|
- `error`
|
|
106
109
|
|
|
107
|
-
When a model provider needs first-time setup
|
|
110
|
+
When a model provider needs first-time setup or reauth, use:
|
|
108
111
|
|
|
109
112
|
```bash
|
|
110
113
|
ouro auth --agent <name>
|
|
111
114
|
ouro auth --agent <name> --provider <provider>
|
|
112
|
-
ouro auth --agent <name> --facing agent --provider <provider>
|
|
113
115
|
```
|
|
114
116
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
`ouro auth` stores credentials only. It does not switch a lane or write provider/model selection.
|
|
118
|
+
|
|
119
|
+
When you want this machine to use a provider/model for a lane, use:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
ouro use --agent <name> --lane <outward|inner> --provider <provider> --model <model>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The outward lane handles user-facing senses. The inner lane handles the agent's private thinking. `ouro use` performs the provider/model check before committing the lane, so a broken local choice fails fast with a repair path instead of surprising the next turn.
|
|
118
126
|
|
|
119
127
|
## Quickstart
|
|
120
128
|
|
|
@@ -159,6 +167,7 @@ ouro logs
|
|
|
159
167
|
ouro stop
|
|
160
168
|
ouro auth --agent <name>
|
|
161
169
|
ouro auth --agent <name> --provider <provider>
|
|
170
|
+
ouro use --agent <name> --lane <outward|inner> --provider <provider> --model <model>
|
|
162
171
|
ouro hatch
|
|
163
172
|
ouro chat <agent>
|
|
164
173
|
ouro msg --to <agent> [--session <id>] [--task <ref>] <message>
|
package/changelog.json
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
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.354",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Provider visibility now surfaces the effective local outward/inner lanes across start-of-turn context, system prompts, pulse, Outlook, and daemon status, using the shared provider binding resolver with safe credential provenance and no raw secret exposure.",
|
|
8
|
+
"Provider auth docs now state the runtime model plainly: `state/providers.json` selects provider/model per machine, `~/.agentsecrets/providers.json` stores machine credentials, `ouro use` switches lanes, and `ouro auth` stores credentials only."
|
|
9
|
+
]
|
|
10
|
+
},
|
|
4
11
|
{
|
|
5
12
|
"version": "0.1.0-alpha.353",
|
|
6
13
|
"changes": [
|
|
@@ -75,15 +75,18 @@ function parseStatusPayload(data) {
|
|
|
75
75
|
const workers = raw.workers;
|
|
76
76
|
const sync = raw.sync;
|
|
77
77
|
const agents = raw.agents;
|
|
78
|
+
const providers = raw.providers;
|
|
78
79
|
if (!overview || typeof overview !== "object" || Array.isArray(overview))
|
|
79
80
|
return null;
|
|
80
81
|
if (!Array.isArray(senses) || !Array.isArray(workers))
|
|
81
82
|
return null;
|
|
82
|
-
// sync and
|
|
83
|
+
// sync, agents, and providers are optional for backward compatibility — older daemons may omit them
|
|
83
84
|
if (sync !== undefined && !Array.isArray(sync))
|
|
84
85
|
return null;
|
|
85
86
|
if (agents !== undefined && !Array.isArray(agents))
|
|
86
87
|
return null;
|
|
88
|
+
if (providers !== undefined && !Array.isArray(providers))
|
|
89
|
+
return null;
|
|
87
90
|
const parsedOverview = {
|
|
88
91
|
daemon: stringField(overview.daemon) ?? "unknown",
|
|
89
92
|
health: stringField(overview.health) ?? "unknown",
|
|
@@ -172,10 +175,38 @@ function parseStatusPayload(data) {
|
|
|
172
175
|
return null;
|
|
173
176
|
return { name, enabled };
|
|
174
177
|
});
|
|
178
|
+
const parsedProviders = (providers ?? []).map((entry) => {
|
|
179
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
180
|
+
return null;
|
|
181
|
+
const row = entry;
|
|
182
|
+
const agent = stringField(row.agent);
|
|
183
|
+
const lane = stringField(row.lane);
|
|
184
|
+
const provider = stringField(row.provider);
|
|
185
|
+
const model = stringField(row.model);
|
|
186
|
+
const source = stringField(row.source);
|
|
187
|
+
const readiness = stringField(row.readiness);
|
|
188
|
+
const credential = stringField(row.credential);
|
|
189
|
+
if (!agent || !lane || !provider || !model || !source || !readiness || !credential)
|
|
190
|
+
return null;
|
|
191
|
+
const parsed = {
|
|
192
|
+
agent,
|
|
193
|
+
lane,
|
|
194
|
+
provider,
|
|
195
|
+
model,
|
|
196
|
+
source,
|
|
197
|
+
readiness,
|
|
198
|
+
credential,
|
|
199
|
+
};
|
|
200
|
+
const detail = stringField(row.detail);
|
|
201
|
+
if (detail !== null)
|
|
202
|
+
parsed.detail = detail;
|
|
203
|
+
return parsed;
|
|
204
|
+
});
|
|
175
205
|
if (parsedSenses.some((row) => row === null) ||
|
|
176
206
|
parsedWorkers.some((row) => row === null) ||
|
|
177
207
|
parsedSync.some((row) => row === null) ||
|
|
178
|
-
parsedAgents.some((row) => row === null)
|
|
208
|
+
parsedAgents.some((row) => row === null) ||
|
|
209
|
+
parsedProviders.some((row) => row === null))
|
|
179
210
|
return null;
|
|
180
211
|
return {
|
|
181
212
|
overview: parsedOverview,
|
|
@@ -183,6 +214,7 @@ function parseStatusPayload(data) {
|
|
|
183
214
|
workers: parsedWorkers,
|
|
184
215
|
sync: parsedSync,
|
|
185
216
|
agents: parsedAgents,
|
|
217
|
+
providers: parsedProviders,
|
|
186
218
|
};
|
|
187
219
|
}
|
|
188
220
|
// ── ANSI color helpers (private) ──
|
|
@@ -208,12 +240,15 @@ function statusDot(status) {
|
|
|
208
240
|
case "ok":
|
|
209
241
|
case "interactive":
|
|
210
242
|
case "enabled":
|
|
243
|
+
case "ready":
|
|
211
244
|
return green("●");
|
|
212
245
|
case "crashed":
|
|
213
246
|
case "warn":
|
|
214
247
|
case "error":
|
|
248
|
+
case "failed":
|
|
215
249
|
return red("●");
|
|
216
250
|
case "needs_config":
|
|
251
|
+
case "stale":
|
|
217
252
|
return yellow("●");
|
|
218
253
|
case "disabled":
|
|
219
254
|
case "stopped":
|
|
@@ -291,6 +326,18 @@ function formatDaemonStatusOutput(response, fallback) {
|
|
|
291
326
|
}
|
|
292
327
|
lines.push("");
|
|
293
328
|
}
|
|
329
|
+
// ── Providers ──
|
|
330
|
+
if (payload.providers.length > 0) {
|
|
331
|
+
lines.push(` ${teal("──")} ${bold("Providers")} ${teal("─".repeat(34))}`);
|
|
332
|
+
const agentLaneWidth = Math.max(16, ...payload.providers.map((r) => `${r.agent} ${r.lane}`.length));
|
|
333
|
+
for (const row of payload.providers) {
|
|
334
|
+
const agentLane = `${row.agent} ${row.lane}`.padEnd(agentLaneWidth);
|
|
335
|
+
const model = `${row.provider} / ${row.model}`;
|
|
336
|
+
const detail = [row.readiness, row.detail, row.source, row.credential].filter(Boolean).join("; ");
|
|
337
|
+
lines.push(` ${agentLane} ${statusDot(row.readiness)} ${model} ${dim(detail)}`);
|
|
338
|
+
}
|
|
339
|
+
lines.push("");
|
|
340
|
+
}
|
|
294
341
|
// ── Senses ──
|
|
295
342
|
if (payload.senses.length > 0) {
|
|
296
343
|
lines.push(` ${teal("──")} ${bold("Senses")} ${teal("─".repeat(37))}`);
|
|
@@ -421,6 +468,7 @@ function buildStoppedStatusPayload(socketPath, syncRows = [], agentRows = []) {
|
|
|
421
468
|
workers: [],
|
|
422
469
|
sync: syncRows,
|
|
423
470
|
agents: agentRows,
|
|
471
|
+
providers: [],
|
|
424
472
|
};
|
|
425
473
|
}
|
|
426
474
|
function daemonUnavailableStatusOutput(socketPath, healthFilePath) {
|
|
@@ -64,6 +64,7 @@ const outlook_http_1 = require("../outlook/outlook-http");
|
|
|
64
64
|
const outlook_types_1 = require("../outlook/outlook-types");
|
|
65
65
|
const outlook_read_1 = require("../outlook/outlook-read");
|
|
66
66
|
const outlook_view_1 = require("../outlook/outlook-view");
|
|
67
|
+
const provider_visibility_1 = require("../provider-visibility");
|
|
67
68
|
const PIDFILE_PATH = path.join(os.homedir(), ".ouro-cli", "daemon.pids");
|
|
68
69
|
/**
|
|
69
70
|
* Defense-in-depth: detect if we're running under vitest. The pidfile lives
|
|
@@ -427,6 +428,10 @@ class OuroDaemon {
|
|
|
427
428
|
const repoRoot = (0, identity_1.getRepoRoot)();
|
|
428
429
|
const sync = (0, agent_discovery_1.listBundleSyncRows)({ bundlesRoot: this.bundlesRoot });
|
|
429
430
|
const agents = (0, agent_discovery_1.listAllBundleAgents)({ bundlesRoot: this.bundlesRoot });
|
|
431
|
+
const providers = agents.flatMap((agent) => (0, provider_visibility_1.providerVisibilityStatusRows)((0, provider_visibility_1.buildAgentProviderVisibility)({
|
|
432
|
+
agentName: agent.name,
|
|
433
|
+
agentRoot: path.join(this.bundlesRoot, `${agent.name}.ouro`),
|
|
434
|
+
})));
|
|
430
435
|
return {
|
|
431
436
|
overview: {
|
|
432
437
|
daemon: "running",
|
|
@@ -443,6 +448,7 @@ class OuroDaemon {
|
|
|
443
448
|
senses,
|
|
444
449
|
sync,
|
|
445
450
|
agents,
|
|
451
|
+
...(providers.length > 0 ? { providers } : {}),
|
|
446
452
|
};
|
|
447
453
|
}
|
|
448
454
|
async start() {
|
|
@@ -52,6 +52,7 @@ const fs = __importStar(require("fs"));
|
|
|
52
52
|
const os = __importStar(require("os"));
|
|
53
53
|
const path = __importStar(require("path"));
|
|
54
54
|
const runtime_1 = require("../../nerves/runtime");
|
|
55
|
+
const provider_visibility_1 = require("../provider-visibility");
|
|
55
56
|
/* v8 ignore next 3 -- path defaults: tests always inject @preserve */
|
|
56
57
|
function defaultPulsePath() {
|
|
57
58
|
return path.join(os.homedir(), ".ouro-cli", "pulse.json");
|
|
@@ -136,7 +137,7 @@ function readAgentActivity(bundlePath, readFile = (p) => fs.readFileSync(p, "utf
|
|
|
136
137
|
* directly, we just compute where it lives so sibling agents have a
|
|
137
138
|
* starting point if they want to navigate there manually.
|
|
138
139
|
*/
|
|
139
|
-
function buildPulseState(snapshots, bundlesRoot, daemonVersion, now, readActivity = readAgentActivity) {
|
|
140
|
+
function buildPulseState(snapshots, bundlesRoot, daemonVersion, now, readActivity = readAgentActivity, readProviderVisibility = () => null) {
|
|
140
141
|
const agents = snapshots.map((snap) => {
|
|
141
142
|
const errorReason = snap.errorReason;
|
|
142
143
|
const bundlePath = path.join(bundlesRoot, `${snap.name}.ouro`);
|
|
@@ -151,6 +152,7 @@ function buildPulseState(snapshots, bundlesRoot, daemonVersion, now, readActivit
|
|
|
151
152
|
// Only read activity for agents that are actually running. For
|
|
152
153
|
// crashed/stopped agents, the runtime.json is stale at best.
|
|
153
154
|
currentActivity: snap.status === "running" ? readActivity(bundlePath) : null,
|
|
155
|
+
providerVisibility: readProviderVisibility(snap.name, bundlePath),
|
|
154
156
|
};
|
|
155
157
|
});
|
|
156
158
|
return {
|
|
@@ -294,7 +296,17 @@ function readPulse(deps = {}) {
|
|
|
294
296
|
return {
|
|
295
297
|
generatedAt: parsed.generatedAt,
|
|
296
298
|
daemonVersion: parsed.daemonVersion,
|
|
297
|
-
agents: parsed.agents.filter(isValidPulseAgentEntry)
|
|
299
|
+
agents: parsed.agents.filter(isValidPulseAgentEntry).map((agent) => {
|
|
300
|
+
const rawAgent = agent;
|
|
301
|
+
if (!Object.prototype.hasOwnProperty.call(rawAgent, "providerVisibility"))
|
|
302
|
+
return agent;
|
|
303
|
+
return {
|
|
304
|
+
...agent,
|
|
305
|
+
providerVisibility: (0, provider_visibility_1.isAgentProviderVisibility)(rawAgent.providerVisibility)
|
|
306
|
+
? rawAgent.providerVisibility
|
|
307
|
+
: null,
|
|
308
|
+
};
|
|
309
|
+
}),
|
|
298
310
|
};
|
|
299
311
|
}
|
|
300
312
|
catch {
|
|
@@ -390,7 +402,7 @@ function pruneDeliveredState(delivered, state) {
|
|
|
390
402
|
* for production callers.
|
|
391
403
|
*/
|
|
392
404
|
function flushPulse(deps) {
|
|
393
|
-
const state = buildPulseState(deps.snapshots, deps.bundlesRoot, deps.daemonVersion, deps.now);
|
|
405
|
+
const state = buildPulseState(deps.snapshots, deps.bundlesRoot, deps.daemonVersion, deps.now, readAgentActivity, (agentName, bundlePath) => (0, provider_visibility_1.buildAgentProviderVisibility)({ agentName, agentRoot: bundlePath }));
|
|
394
406
|
/* v8 ignore start -- dep defaults: production daemon path; the arrow functions only fire when the corresponding dep is omitted, which only happens in production code paths. Tests inject all deps explicitly. @preserve */
|
|
395
407
|
const readPrev = deps.readPrev ?? (() => readPulse());
|
|
396
408
|
const writeNext = deps.writeNext ?? ((s) => writePulse(s));
|
|
@@ -173,6 +173,7 @@ function buildOutlookAgentView(input) {
|
|
|
173
173
|
agentRoot: input.agent.agentRoot,
|
|
174
174
|
enabled: input.agent.enabled,
|
|
175
175
|
provider: input.agent.provider,
|
|
176
|
+
providers: input.agent.providers ?? null,
|
|
176
177
|
senses: input.agent.senses,
|
|
177
178
|
freshness: input.agent.freshness,
|
|
178
179
|
degraded: input.agent.degraded,
|
|
@@ -46,6 +46,7 @@ const session_activity_1 = require("../../session-activity");
|
|
|
46
46
|
const identity_1 = require("../../identity");
|
|
47
47
|
const agent_discovery_1 = require("../../daemon/agent-discovery");
|
|
48
48
|
const runtime_metadata_1 = require("../../daemon/runtime-metadata");
|
|
49
|
+
const provider_visibility_1 = require("../../provider-visibility");
|
|
49
50
|
const thoughts_1 = require("../../daemon/thoughts");
|
|
50
51
|
const outlook_types_1 = require("../outlook-types");
|
|
51
52
|
const shared_1 = require("./shared");
|
|
@@ -266,6 +267,7 @@ function summarizeAgent(state) {
|
|
|
266
267
|
return {
|
|
267
268
|
agentName: state.agentName,
|
|
268
269
|
enabled: state.enabled,
|
|
270
|
+
providers: state.providers,
|
|
269
271
|
freshness: state.freshness,
|
|
270
272
|
degraded: state.degraded,
|
|
271
273
|
tasks: {
|
|
@@ -296,6 +298,7 @@ function readOutlookAgentState(agentName, options = {}) {
|
|
|
296
298
|
issues.push(...inner.issues);
|
|
297
299
|
const coding = readCodingSummary(agentRoot);
|
|
298
300
|
issues.push(...coding.issues);
|
|
301
|
+
const providers = (0, provider_visibility_1.buildAgentProviderVisibility)({ agentName, agentRoot, homeDir: options.homeDir });
|
|
299
302
|
const latestActivityAt = collectLatestActivityTimestamps({
|
|
300
303
|
obligations: obligations.items,
|
|
301
304
|
sessions: sessions.items,
|
|
@@ -308,6 +311,7 @@ function readOutlookAgentState(agentName, options = {}) {
|
|
|
308
311
|
agentRoot,
|
|
309
312
|
enabled: config.summary.enabled,
|
|
310
313
|
provider: config.summary.provider,
|
|
314
|
+
providers,
|
|
311
315
|
senses: config.summary.senses,
|
|
312
316
|
freshness: summarizeFreshness(latestActivityAt, now),
|
|
313
317
|
degraded: summarizeDegraded(issues),
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildAgentProviderVisibility = buildAgentProviderVisibility;
|
|
4
|
+
exports.formatProviderVisibilityLine = formatProviderVisibilityLine;
|
|
5
|
+
exports.formatAgentProviderVisibilityForPrompt = formatAgentProviderVisibilityForPrompt;
|
|
6
|
+
exports.formatAgentProviderVisibilityForStartOfTurn = formatAgentProviderVisibilityForStartOfTurn;
|
|
7
|
+
exports.formatAgentProviderVisibilityForPulse = formatAgentProviderVisibilityForPulse;
|
|
8
|
+
exports.providerVisibilityStatusRows = providerVisibilityStatusRows;
|
|
9
|
+
exports.isAgentProviderVisibility = isAgentProviderVisibility;
|
|
10
|
+
const runtime_1 = require("../nerves/runtime");
|
|
11
|
+
const provider_binding_resolver_1 = require("./provider-binding-resolver");
|
|
12
|
+
const LANES = ["outward", "inner"];
|
|
13
|
+
function credentialVisibility(binding) {
|
|
14
|
+
const credential = binding.credential;
|
|
15
|
+
if (credential.status === "present") {
|
|
16
|
+
return {
|
|
17
|
+
status: "present",
|
|
18
|
+
source: credential.source,
|
|
19
|
+
contributedByAgent: credential.contributedByAgent,
|
|
20
|
+
revision: credential.revision,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
status: credential.status,
|
|
25
|
+
repairCommand: credential.repair.command,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function readinessVisibility(binding) {
|
|
29
|
+
return {
|
|
30
|
+
status: binding.readiness.status,
|
|
31
|
+
...(binding.readiness.checkedAt ? { checkedAt: binding.readiness.checkedAt } : {}),
|
|
32
|
+
...(binding.readiness.error ? { error: binding.readiness.error } : {}),
|
|
33
|
+
...(binding.readiness.reason ? { reason: binding.readiness.reason } : {}),
|
|
34
|
+
...(binding.readiness.attempts !== undefined ? { attempts: binding.readiness.attempts } : {}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function visibilityForLane(input, lane) {
|
|
38
|
+
const resolved = (0, provider_binding_resolver_1.resolveEffectiveProviderBinding)({ ...input, lane });
|
|
39
|
+
if (!resolved.ok) {
|
|
40
|
+
return {
|
|
41
|
+
lane,
|
|
42
|
+
status: "unconfigured",
|
|
43
|
+
provider: "unconfigured",
|
|
44
|
+
model: "-",
|
|
45
|
+
source: "missing",
|
|
46
|
+
readiness: {
|
|
47
|
+
status: "unknown",
|
|
48
|
+
reason: resolved.reason,
|
|
49
|
+
},
|
|
50
|
+
credential: {
|
|
51
|
+
status: "missing",
|
|
52
|
+
repairCommand: resolved.repair.command,
|
|
53
|
+
},
|
|
54
|
+
repairCommand: resolved.repair.command,
|
|
55
|
+
reason: resolved.reason,
|
|
56
|
+
warnings: resolved.warnings.map((warning) => warning.message),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
lane,
|
|
61
|
+
status: "configured",
|
|
62
|
+
provider: resolved.binding.provider,
|
|
63
|
+
model: resolved.binding.model,
|
|
64
|
+
source: resolved.binding.source,
|
|
65
|
+
readiness: readinessVisibility(resolved.binding),
|
|
66
|
+
credential: credentialVisibility(resolved.binding),
|
|
67
|
+
warnings: resolved.binding.warnings.map((warning) => warning.message),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function buildAgentProviderVisibility(input) {
|
|
71
|
+
const visibility = {
|
|
72
|
+
agentName: input.agentName,
|
|
73
|
+
lanes: LANES.map((lane) => visibilityForLane(input, lane)),
|
|
74
|
+
};
|
|
75
|
+
(0, runtime_1.emitNervesEvent)({
|
|
76
|
+
component: "config/identity",
|
|
77
|
+
event: "config.provider_visibility_built",
|
|
78
|
+
message: "built provider visibility summary",
|
|
79
|
+
meta: {
|
|
80
|
+
agentName: input.agentName,
|
|
81
|
+
laneStatuses: visibility.lanes.map((lane) => `${lane.lane}:${lane.status}`).join(","),
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
return visibility;
|
|
85
|
+
}
|
|
86
|
+
function credentialLabel(credential) {
|
|
87
|
+
if (credential.status === "present") {
|
|
88
|
+
const source = credential.source ?? "unknown";
|
|
89
|
+
return credential.contributedByAgent ? `${source} from ${credential.contributedByAgent}` : source;
|
|
90
|
+
}
|
|
91
|
+
if (credential.status === "invalid-pool")
|
|
92
|
+
return "invalid pool";
|
|
93
|
+
return "missing";
|
|
94
|
+
}
|
|
95
|
+
function readinessLabel(readiness) {
|
|
96
|
+
if (readiness.status === "failed") {
|
|
97
|
+
return readiness.error ? `failed: ${readiness.error}` : "failed";
|
|
98
|
+
}
|
|
99
|
+
if (readiness.status === "stale") {
|
|
100
|
+
return readiness.reason ? `stale: ${readiness.reason}` : "stale";
|
|
101
|
+
}
|
|
102
|
+
if (readiness.status === "unknown") {
|
|
103
|
+
return readiness.reason ? `unknown: ${readiness.reason}` : "unknown";
|
|
104
|
+
}
|
|
105
|
+
return readiness.status;
|
|
106
|
+
}
|
|
107
|
+
function formatProviderVisibilityLine(lane) {
|
|
108
|
+
if (lane.status === "unconfigured") {
|
|
109
|
+
return `${lane.lane}: unconfigured (${lane.reason}); repair: ${lane.repairCommand}`;
|
|
110
|
+
}
|
|
111
|
+
const parts = [
|
|
112
|
+
readinessLabel(lane.readiness),
|
|
113
|
+
`source: ${lane.source}`,
|
|
114
|
+
`credentials: ${credentialLabel(lane.credential)}`,
|
|
115
|
+
];
|
|
116
|
+
if (lane.credential.revision)
|
|
117
|
+
parts.push(`revision: ${lane.credential.revision}`);
|
|
118
|
+
if (lane.credential.repairCommand)
|
|
119
|
+
parts.push(`repair: ${lane.credential.repairCommand}`);
|
|
120
|
+
if (lane.warnings.length > 0)
|
|
121
|
+
parts.push(`warnings: ${lane.warnings.join("; ")}`);
|
|
122
|
+
return `${lane.lane}: ${lane.provider} / ${lane.model} [${parts.join("; ")}]`;
|
|
123
|
+
}
|
|
124
|
+
function formatAgentProviderVisibilityForPrompt(visibility) {
|
|
125
|
+
if (visibility.lanes.every((lane) => lane.status === "unconfigured")) {
|
|
126
|
+
return [
|
|
127
|
+
"provider bindings are not configured on this machine.",
|
|
128
|
+
...visibility.lanes.map((lane) => `- ${formatProviderVisibilityLine(lane)}`),
|
|
129
|
+
].join("\n");
|
|
130
|
+
}
|
|
131
|
+
return [
|
|
132
|
+
"runtime uses local provider bindings for this machine:",
|
|
133
|
+
...visibility.lanes.map((lane) => `- ${formatProviderVisibilityLine(lane)}`),
|
|
134
|
+
].join("\n");
|
|
135
|
+
}
|
|
136
|
+
function formatAgentProviderVisibilityForStartOfTurn(visibility) {
|
|
137
|
+
return visibility.lanes.map((lane) => `- ${formatProviderVisibilityLine(lane)}`).join("\n");
|
|
138
|
+
}
|
|
139
|
+
function formatAgentProviderVisibilityForPulse(visibility) {
|
|
140
|
+
return visibility.lanes.map((lane) => formatProviderVisibilityLine(lane)).join("; ");
|
|
141
|
+
}
|
|
142
|
+
function providerVisibilityStatusRows(visibility) {
|
|
143
|
+
return visibility.lanes.map((lane) => {
|
|
144
|
+
if (lane.status === "unconfigured") {
|
|
145
|
+
return {
|
|
146
|
+
agent: visibility.agentName,
|
|
147
|
+
lane: lane.lane,
|
|
148
|
+
provider: "unconfigured",
|
|
149
|
+
model: "-",
|
|
150
|
+
source: "missing",
|
|
151
|
+
readiness: "unknown",
|
|
152
|
+
detail: lane.repairCommand,
|
|
153
|
+
credential: "missing",
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
agent: visibility.agentName,
|
|
158
|
+
lane: lane.lane,
|
|
159
|
+
provider: lane.provider,
|
|
160
|
+
model: lane.model,
|
|
161
|
+
source: lane.source,
|
|
162
|
+
readiness: lane.readiness.status,
|
|
163
|
+
...(lane.readiness.error ? { detail: lane.readiness.error } : {}),
|
|
164
|
+
credential: credentialLabel(lane.credential),
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
function isAgentProviderVisibility(value) {
|
|
169
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
170
|
+
return false;
|
|
171
|
+
const record = value;
|
|
172
|
+
if (typeof record.agentName !== "string")
|
|
173
|
+
return false;
|
|
174
|
+
if (!Array.isArray(record.lanes))
|
|
175
|
+
return false;
|
|
176
|
+
return record.lanes.every((lane) => {
|
|
177
|
+
if (!lane || typeof lane !== "object" || Array.isArray(lane))
|
|
178
|
+
return false;
|
|
179
|
+
const laneRecord = lane;
|
|
180
|
+
return (laneRecord.lane === "outward" || laneRecord.lane === "inner")
|
|
181
|
+
&& (laneRecord.status === "configured" || laneRecord.status === "unconfigured");
|
|
182
|
+
});
|
|
183
|
+
}
|
|
@@ -228,6 +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.providerState ?? "", priority: 8 },
|
|
231
232
|
{ label: "bundleState", content: (0, bundle_state_1.renderBundleStateHint)(packet.bundleState ?? []), priority: 7 },
|
|
232
233
|
{ label: "syncFailure", content: packet.syncFailure ?? "", priority: 7 },
|
|
233
234
|
{ label: "resume", content: packet.resumeHint, priority: 6 },
|
|
@@ -305,6 +306,9 @@ function formatSections(sections) {
|
|
|
305
306
|
case "bundleState":
|
|
306
307
|
parts.push(`**Bundle:** ${section.content}`);
|
|
307
308
|
break;
|
|
309
|
+
case "provider":
|
|
310
|
+
parts.push(`**Provider:**\n${section.content}`);
|
|
311
|
+
break;
|
|
308
312
|
}
|
|
309
313
|
}
|
|
310
314
|
return parts.join("\n\n");
|
|
@@ -59,6 +59,7 @@ const cares_1 = require("../arc/cares");
|
|
|
59
59
|
const config_1 = require("./config");
|
|
60
60
|
const daemon_health_1 = require("./daemon/daemon-health");
|
|
61
61
|
const prompt_1 = require("../mind/prompt");
|
|
62
|
+
const provider_visibility_1 = require("./provider-visibility");
|
|
62
63
|
// ── Helpers ─────────────────────────────────────────────────────────
|
|
63
64
|
const DAEMON_SOCKET_PATH = "/tmp/ouroboros-daemon.sock";
|
|
64
65
|
function isLiveCodingSessionStatus(status) {
|
|
@@ -306,6 +307,7 @@ async function buildTurnContext(input) {
|
|
|
306
307
|
/* v8 ignore stop */
|
|
307
308
|
}
|
|
308
309
|
const bundleMeta = readBundleMetaFile();
|
|
310
|
+
const providerVisibility = (0, provider_visibility_1.buildAgentProviderVisibility)({ agentName, agentRoot });
|
|
309
311
|
let daemonHealth = null;
|
|
310
312
|
try {
|
|
311
313
|
daemonHealth = (0, daemon_health_1.readHealth)((0, daemon_health_1.getDefaultHealthPath)());
|
|
@@ -333,6 +335,7 @@ async function buildTurnContext(input) {
|
|
|
333
335
|
bridgeCount: activeBridges.length,
|
|
334
336
|
codingSessionCount: codingSessions.length,
|
|
335
337
|
episodeCount: recentEpisodes.length,
|
|
338
|
+
providerLaneCount: providerVisibility.lanes.length,
|
|
336
339
|
},
|
|
337
340
|
});
|
|
338
341
|
return {
|
|
@@ -349,6 +352,7 @@ async function buildTurnContext(input) {
|
|
|
349
352
|
activeCares,
|
|
350
353
|
syncConfig,
|
|
351
354
|
syncFailure: undefined, // Set by pipeline after preTurnPull
|
|
355
|
+
providerVisibility,
|
|
352
356
|
daemonRunning,
|
|
353
357
|
senseStatusLines,
|
|
354
358
|
bundleMeta,
|
package/dist/mind/prompt.js
CHANGED
|
@@ -78,6 +78,7 @@ const obligation_steering_1 = require("./obligation-steering");
|
|
|
78
78
|
const daemon_health_1 = require("../heart/daemon/daemon-health");
|
|
79
79
|
const scrutiny_1 = require("./scrutiny");
|
|
80
80
|
const pulse_1 = require("../heart/daemon/pulse");
|
|
81
|
+
const provider_visibility_1 = require("../heart/provider-visibility");
|
|
81
82
|
// Lazy-loaded psyche text cache
|
|
82
83
|
let _psycheCache = null;
|
|
83
84
|
let _senseStatusLinesCache = null;
|
|
@@ -459,7 +460,10 @@ function senseRuntimeGuidance(channel, preReadStatusLines) {
|
|
|
459
460
|
}
|
|
460
461
|
return lines;
|
|
461
462
|
}
|
|
462
|
-
function providerSection(channel) {
|
|
463
|
+
function providerSection(channel, options) {
|
|
464
|
+
if (options?.providerVisibility) {
|
|
465
|
+
return `## my provider\n${(0, provider_visibility_1.formatAgentProviderVisibilityForPrompt)(options.providerVisibility)}`;
|
|
466
|
+
}
|
|
463
467
|
return `## my provider\n${(0, core_1.getProviderDisplayLabel)((0, channel_1.channelToFacing)(channel))}`;
|
|
464
468
|
}
|
|
465
469
|
function dateSection() {
|
|
@@ -727,6 +731,8 @@ function pulseSection(channel = "cli") {
|
|
|
727
731
|
if (sib.fixHint)
|
|
728
732
|
lines.push(` fix: ${sib.fixHint}`);
|
|
729
733
|
lines.push(` bundle: \`${sib.bundlePath}\``);
|
|
734
|
+
if (sib.providerVisibility)
|
|
735
|
+
lines.push(` provider: ${(0, provider_visibility_1.formatAgentProviderVisibilityForPulse)(sib.providerVisibility)}`);
|
|
730
736
|
}
|
|
731
737
|
lines.push("");
|
|
732
738
|
}
|
|
@@ -737,6 +743,8 @@ function pulseSection(channel = "cli") {
|
|
|
737
743
|
for (const sib of healthy) {
|
|
738
744
|
const activity = sib.currentActivity ? ` — ${sib.currentActivity}` : "";
|
|
739
745
|
lines.push(`- **${sib.name}** is running${activity}. bundle: \`${sib.bundlePath}\``);
|
|
746
|
+
if (sib.providerVisibility)
|
|
747
|
+
lines.push(` provider: ${(0, provider_visibility_1.formatAgentProviderVisibilityForPulse)(sib.providerVisibility)}`);
|
|
740
748
|
}
|
|
741
749
|
lines.push("");
|
|
742
750
|
}
|
|
@@ -744,6 +752,8 @@ function pulseSection(channel = "cli") {
|
|
|
744
752
|
lines.push("**idle siblings** — configured but not currently running:");
|
|
745
753
|
for (const sib of idle) {
|
|
746
754
|
lines.push(`- **${sib.name}** (status: ${sib.status}). bundle: \`${sib.bundlePath}\``);
|
|
755
|
+
if (sib.providerVisibility)
|
|
756
|
+
lines.push(` provider: ${(0, provider_visibility_1.formatAgentProviderVisibilityForPulse)(sib.providerVisibility)}`);
|
|
747
757
|
}
|
|
748
758
|
lines.push("");
|
|
749
759
|
}
|
|
@@ -1260,7 +1270,7 @@ async function buildSystem(channel = "cli", options, context) {
|
|
|
1260
1270
|
runtimeInfoSection(channel, options),
|
|
1261
1271
|
rhythmStatusSection(options?.daemonHealth),
|
|
1262
1272
|
channelNatureSection((0, channel_1.getChannelCapabilities)(channel)),
|
|
1263
|
-
providerSection(channel),
|
|
1273
|
+
providerSection(channel, options),
|
|
1264
1274
|
dateSection(),
|
|
1265
1275
|
// Group 3: my tools & capabilities
|
|
1266
1276
|
"# my tools & capabilities",
|
package/dist/senses/pipeline.js
CHANGED
|
@@ -63,6 +63,7 @@ const session_events_1 = require("../heart/session-events");
|
|
|
63
63
|
const presence_1 = require("../arc/presence");
|
|
64
64
|
const episodes_1 = require("../arc/episodes");
|
|
65
65
|
const turn_context_1 = require("../heart/turn-context");
|
|
66
|
+
const provider_visibility_1 = require("../heart/provider-visibility");
|
|
66
67
|
/**
|
|
67
68
|
* Emit episodes for obligation state transitions detected during a turn.
|
|
68
69
|
* Exported for direct testability (avoids v8 coverage merge issues in multi-file test suites).
|
|
@@ -435,6 +436,9 @@ async function handleInboundTurn(input) {
|
|
|
435
436
|
if (syncFailure) {
|
|
436
437
|
startOfTurnPacket.syncFailure = syncFailure;
|
|
437
438
|
}
|
|
439
|
+
if (ctx.providerVisibility) {
|
|
440
|
+
startOfTurnPacket.providerState = (0, provider_visibility_1.formatAgentProviderVisibilityForStartOfTurn)(ctx.providerVisibility);
|
|
441
|
+
}
|
|
438
442
|
// Structured bundle state detection — surfaces discrete issues the
|
|
439
443
|
// agent can remediate via the bundle_* tools. Runs independently of
|
|
440
444
|
// syncFailure so the two signals coexist during the transition away
|
|
@@ -489,6 +493,7 @@ async function handleInboundTurn(input) {
|
|
|
489
493
|
bundleMeta: ctx.bundleMeta,
|
|
490
494
|
daemonHealth: ctx.daemonHealth,
|
|
491
495
|
journalFiles: ctx.journalFiles,
|
|
496
|
+
...(ctx.providerVisibility ? { providerVisibility: ctx.providerVisibility } : {}),
|
|
492
497
|
toolContext: {
|
|
493
498
|
/* v8 ignore next -- default no-op signin satisfies interface; real signin injected by sense adapter @preserve */
|
|
494
499
|
signin: async () => undefined,
|