@ouro.bot/cli 0.1.0-alpha.158 → 0.1.0-alpha.159
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/AdoptionSpecialist.ouro/agent.json +3 -2
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +1 -1
- package/README.md +5 -4
- package/changelog.json +10 -0
- package/dist/heart/config.js +0 -7
- package/dist/heart/core.js +51 -36
- package/dist/heart/daemon/auth-flow.js +45 -25
- package/dist/heart/daemon/daemon-cli.js +36 -10
- package/dist/heart/daemon/daemon.js +2 -0
- package/dist/heart/daemon/hatch-flow.js +2 -0
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/specialist-prompt.js +3 -1
- package/dist/heart/identity.js +78 -40
- package/dist/heart/migrate-config.js +127 -0
- package/dist/heart/provider-ping.js +19 -6
- package/dist/heart/providers/anthropic.js +9 -10
- package/dist/heart/providers/azure.js +5 -5
- package/dist/heart/providers/github-copilot.js +6 -6
- package/dist/heart/providers/minimax.js +4 -4
- package/dist/heart/providers/openai-codex.js +6 -6
- package/dist/mind/friends/channel.js +12 -0
- package/dist/mind/prompt.js +3 -3
- package/dist/senses/bluebubbles-client.js +1 -1
- package/dist/senses/bluebubbles.js +1 -1
- package/dist/senses/cli.js +4 -2
- package/dist/senses/pipeline.js +3 -2
- package/dist/senses/teams.js +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version":
|
|
2
|
+
"version": 2,
|
|
3
3
|
"enabled": false,
|
|
4
|
-
"provider": "anthropic",
|
|
4
|
+
"humanFacing": { "provider": "anthropic", "model": "claude-opus-4-6" },
|
|
5
|
+
"agentFacing": { "provider": "anthropic", "model": "claude-opus-4-6" },
|
|
5
6
|
"context": {
|
|
6
7
|
"maxTokens": 80000,
|
|
7
8
|
"contextMargin": 20
|
|
@@ -16,7 +16,7 @@ I help humans hatch new agent partners. I am one of thirteen serpent guides —
|
|
|
16
16
|
1. Confirm provider setup and usable credentials.
|
|
17
17
|
2. Interview for goals, style, and operating context.
|
|
18
18
|
3. Synthesize a concrete hatchling identity and bootstrap files.
|
|
19
|
-
4. Create canonical bundle structure with required defaults.
|
|
19
|
+
4. Create canonical bundle structure with required defaults, configuring both `humanFacing` and `agentFacing` provider+model settings.
|
|
20
20
|
5. Introduce the hatchling and hand off clearly.
|
|
21
21
|
|
|
22
22
|
## Filesystem orientation
|
package/README.md
CHANGED
|
@@ -90,7 +90,7 @@ Task docs do not live in this repo anymore. Planning and doing docs live in the
|
|
|
90
90
|
|
|
91
91
|
## Runtime Truths
|
|
92
92
|
|
|
93
|
-
- `agent.json` is the source of truth for provider selection, phrase pools, context settings, and enabled senses.
|
|
93
|
+
- `agent.json` is the source of truth for provider+model selection per facing (`humanFacing` and `agentFacing`), phrase pools, context settings, and enabled senses.
|
|
94
94
|
- `configPath` must point to `~/.agentsecrets/<agent>/secrets.json`.
|
|
95
95
|
- The daemon discovers bundles dynamically from `~/AgentBundles`.
|
|
96
96
|
- `ouro status` reports version, last-updated time, discovered agents, senses, and workers.
|
|
@@ -109,11 +109,12 @@ When a model provider needs first-time setup, reauth, or an explicit switch, use
|
|
|
109
109
|
```bash
|
|
110
110
|
ouro auth --agent <name>
|
|
111
111
|
ouro auth --agent <name> --provider <provider>
|
|
112
|
+
ouro auth --agent <name> --facing agent --provider <provider>
|
|
112
113
|
```
|
|
113
114
|
|
|
114
|
-
The default form reauths the provider already selected in `agent.json`. The
|
|
115
|
-
`--provider` form is for adding or switching providers
|
|
116
|
-
|
|
115
|
+
The default form reauths the human-facing provider already selected in `agent.json`. The
|
|
116
|
+
`--provider` form is for adding or switching providers. The `--facing` flag (values: `human`
|
|
117
|
+
or `agent`) controls which facing gets updated; it defaults to `human` when omitted.
|
|
117
118
|
|
|
118
119
|
## Quickstart
|
|
119
120
|
|
package/changelog.json
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
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.159",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Human-facing and agent-facing provider configs: agent.json now has humanFacing and agentFacing blocks, each with provider and model. CLI/Teams/BlueBubbles use humanFacing, inner dialog uses agentFacing.",
|
|
8
|
+
"Model moved from secrets.json to agent.json. Secrets now store credentials only (API keys, tokens, endpoints).",
|
|
9
|
+
"Automatic v1 to v2 migration: existing bundles upgraded via UpdateHook at daemon startup and inline in loadAgentConfig/readAgentConfigForAgent.",
|
|
10
|
+
"CLI commands support --facing human|agent flag for config model and auth switch.",
|
|
11
|
+
"Provider factories accept model as parameter instead of reading from secrets config."
|
|
12
|
+
]
|
|
13
|
+
},
|
|
4
14
|
{
|
|
5
15
|
"version": "0.1.0-alpha.158",
|
|
6
16
|
"changes": [
|
package/dist/heart/config.js
CHANGED
|
@@ -63,10 +63,7 @@ const identity_1 = require("./identity");
|
|
|
63
63
|
const runtime_1 = require("../nerves/runtime");
|
|
64
64
|
const DEFAULT_SECRETS_TEMPLATE = {
|
|
65
65
|
providers: {
|
|
66
|
-
// Keep provider field ordering consistent: model first, then auth credentials,
|
|
67
|
-
// then provider-specific transport fields.
|
|
68
66
|
azure: {
|
|
69
|
-
modelName: "",
|
|
70
67
|
apiKey: "",
|
|
71
68
|
endpoint: "",
|
|
72
69
|
deployment: "",
|
|
@@ -74,19 +71,15 @@ const DEFAULT_SECRETS_TEMPLATE = {
|
|
|
74
71
|
managedIdentityClientId: "",
|
|
75
72
|
},
|
|
76
73
|
minimax: {
|
|
77
|
-
model: "",
|
|
78
74
|
apiKey: "",
|
|
79
75
|
},
|
|
80
76
|
anthropic: {
|
|
81
|
-
model: "claude-opus-4-6",
|
|
82
77
|
setupToken: "",
|
|
83
78
|
},
|
|
84
79
|
"openai-codex": {
|
|
85
|
-
model: "gpt-5.4",
|
|
86
80
|
oauthAccessToken: "",
|
|
87
81
|
},
|
|
88
82
|
"github-copilot": {
|
|
89
|
-
model: "claude-sonnet-4.6",
|
|
90
83
|
githubToken: "",
|
|
91
84
|
baseUrl: "",
|
|
92
85
|
},
|
package/dist/heart/core.js
CHANGED
|
@@ -33,30 +33,36 @@ const identity_2 = require("./identity");
|
|
|
33
33
|
const socket_client_1 = require("./daemon/socket-client");
|
|
34
34
|
const obligations_1 = require("./obligations");
|
|
35
35
|
const tool_loop_1 = require("./tool-loop");
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
const _providerRuntimes = {
|
|
37
|
+
human: null,
|
|
38
|
+
agent: null,
|
|
39
|
+
};
|
|
40
|
+
function getProviderRuntimeFingerprint(facing) {
|
|
41
|
+
const config = (0, identity_1.loadAgentConfig)();
|
|
42
|
+
const facingConfig = facing === "human" ? config.humanFacing : config.agentFacing;
|
|
43
|
+
const provider = facingConfig.provider;
|
|
44
|
+
const model = facingConfig.model;
|
|
39
45
|
/* v8 ignore next -- switch: not all provider branches exercised in CI @preserve */
|
|
40
46
|
switch (provider) {
|
|
41
47
|
case "azure": {
|
|
42
|
-
const { apiKey, endpoint, deployment,
|
|
43
|
-
return JSON.stringify({ provider, apiKey, endpoint, deployment,
|
|
48
|
+
const { apiKey, endpoint, deployment, apiVersion, managedIdentityClientId } = (0, config_1.getAzureConfig)();
|
|
49
|
+
return JSON.stringify({ provider, model, apiKey, endpoint, deployment, apiVersion, managedIdentityClientId });
|
|
44
50
|
}
|
|
45
51
|
case "anthropic": {
|
|
46
|
-
const {
|
|
52
|
+
const { setupToken } = (0, config_1.getAnthropicConfig)();
|
|
47
53
|
return JSON.stringify({ provider, model, setupToken });
|
|
48
54
|
}
|
|
49
55
|
case "minimax": {
|
|
50
|
-
const { apiKey
|
|
51
|
-
return JSON.stringify({ provider,
|
|
56
|
+
const { apiKey } = (0, config_1.getMinimaxConfig)();
|
|
57
|
+
return JSON.stringify({ provider, model, apiKey });
|
|
52
58
|
}
|
|
53
59
|
case "openai-codex": {
|
|
54
|
-
const {
|
|
60
|
+
const { oauthAccessToken } = (0, config_1.getOpenAICodexConfig)();
|
|
55
61
|
return JSON.stringify({ provider, model, oauthAccessToken });
|
|
56
62
|
}
|
|
57
63
|
/* v8 ignore start -- fingerprint: tested via provider init tests @preserve */
|
|
58
64
|
case "github-copilot": {
|
|
59
|
-
const {
|
|
65
|
+
const { githubToken, baseUrl } = (0, config_1.getGithubCopilotConfig)();
|
|
60
66
|
return JSON.stringify({ provider, model, githubToken, baseUrl });
|
|
61
67
|
}
|
|
62
68
|
/* v8 ignore stop */
|
|
@@ -71,18 +77,22 @@ function createProviderRegistry() {
|
|
|
71
77
|
"github-copilot": github_copilot_1.createGithubCopilotProviderRuntime,
|
|
72
78
|
};
|
|
73
79
|
return {
|
|
74
|
-
resolve() {
|
|
75
|
-
const
|
|
76
|
-
|
|
80
|
+
resolve(provider, model) {
|
|
81
|
+
const resolvedProvider = provider ?? (0, identity_1.loadAgentConfig)().humanFacing.provider;
|
|
82
|
+
const resolvedModel = model ?? (0, identity_1.loadAgentConfig)().humanFacing.model;
|
|
83
|
+
return factories[resolvedProvider](resolvedModel);
|
|
77
84
|
},
|
|
78
85
|
};
|
|
79
86
|
}
|
|
80
|
-
function getProviderRuntime() {
|
|
87
|
+
function getProviderRuntime(facing = "human") {
|
|
81
88
|
try {
|
|
82
|
-
const fingerprint = getProviderRuntimeFingerprint();
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
const fingerprint = getProviderRuntimeFingerprint(facing);
|
|
90
|
+
const cached = _providerRuntimes[facing];
|
|
91
|
+
if (!cached || cached.fingerprint !== fingerprint) {
|
|
92
|
+
const config = (0, identity_1.loadAgentConfig)();
|
|
93
|
+
const facingConfig = facing === "human" ? config.humanFacing : config.agentFacing;
|
|
94
|
+
const runtime = createProviderRegistry().resolve(facingConfig.provider, facingConfig.model);
|
|
95
|
+
_providerRuntimes[facing] = runtime ? { fingerprint, runtime } : null;
|
|
86
96
|
}
|
|
87
97
|
}
|
|
88
98
|
catch (error) {
|
|
@@ -99,7 +109,7 @@ function getProviderRuntime() {
|
|
|
99
109
|
process.exit(1);
|
|
100
110
|
throw new Error("unreachable");
|
|
101
111
|
}
|
|
102
|
-
if (!
|
|
112
|
+
if (!_providerRuntimes[facing]) {
|
|
103
113
|
(0, runtime_1.emitNervesEvent)({
|
|
104
114
|
level: "error",
|
|
105
115
|
event: "engine.provider_init_error",
|
|
@@ -110,7 +120,7 @@ function getProviderRuntime() {
|
|
|
110
120
|
process.exit(1);
|
|
111
121
|
throw new Error("unreachable");
|
|
112
122
|
}
|
|
113
|
-
return
|
|
123
|
+
return _providerRuntimes[facing].runtime;
|
|
114
124
|
}
|
|
115
125
|
/**
|
|
116
126
|
* Clear the cached provider runtime so the next access re-creates it from
|
|
@@ -118,17 +128,18 @@ function getProviderRuntime() {
|
|
|
118
128
|
* provider fingerprint changes on disk.
|
|
119
129
|
*/
|
|
120
130
|
function resetProviderRuntime() {
|
|
121
|
-
|
|
131
|
+
_providerRuntimes.human = null;
|
|
132
|
+
_providerRuntimes.agent = null;
|
|
122
133
|
}
|
|
123
|
-
function getModel() {
|
|
124
|
-
return getProviderRuntime().model;
|
|
134
|
+
function getModel(facing = "human") {
|
|
135
|
+
return getProviderRuntime(facing).model;
|
|
125
136
|
}
|
|
126
|
-
function getProvider() {
|
|
127
|
-
return getProviderRuntime().id;
|
|
137
|
+
function getProvider(facing = "human") {
|
|
138
|
+
return getProviderRuntime(facing).id;
|
|
128
139
|
}
|
|
129
|
-
function createSummarize() {
|
|
140
|
+
function createSummarize(facing = "human") {
|
|
130
141
|
return async (transcript, instruction) => {
|
|
131
|
-
const runtime = getProviderRuntime();
|
|
142
|
+
const runtime = getProviderRuntime(facing);
|
|
132
143
|
const client = runtime.client;
|
|
133
144
|
const response = await client.chat.completions.create({
|
|
134
145
|
model: runtime.model,
|
|
@@ -141,18 +152,21 @@ function createSummarize() {
|
|
|
141
152
|
return response.choices?.[0]?.message?.content ?? transcript;
|
|
142
153
|
};
|
|
143
154
|
}
|
|
144
|
-
function getProviderDisplayLabel() {
|
|
145
|
-
const
|
|
155
|
+
function getProviderDisplayLabel(facing = "human") {
|
|
156
|
+
const config = (0, identity_1.loadAgentConfig)();
|
|
157
|
+
const facingConfig = facing === "human" ? config.humanFacing : config.agentFacing;
|
|
158
|
+
const provider = facingConfig.provider;
|
|
159
|
+
const model = facingConfig.model || "unknown";
|
|
146
160
|
const providerLabelBuilders = {
|
|
147
161
|
azure: () => {
|
|
148
|
-
const
|
|
149
|
-
return `azure openai (${
|
|
162
|
+
const azureCfg = (0, config_1.getAzureConfig)();
|
|
163
|
+
return `azure openai (${azureCfg.deployment || "default"}, model: ${model})`;
|
|
150
164
|
},
|
|
151
|
-
anthropic: () => `anthropic (${
|
|
152
|
-
minimax: () => `minimax (${
|
|
153
|
-
"openai-codex": () => `openai codex (${
|
|
165
|
+
anthropic: () => `anthropic (${model})`,
|
|
166
|
+
minimax: () => `minimax (${model})`,
|
|
167
|
+
"openai-codex": () => `openai codex (${model})`,
|
|
154
168
|
/* v8 ignore next -- branch: tested via display label unit test @preserve */
|
|
155
|
-
"github-copilot": () => `github copilot (${
|
|
169
|
+
"github-copilot": () => `github copilot (${model})`,
|
|
156
170
|
};
|
|
157
171
|
return providerLabelBuilders[provider]();
|
|
158
172
|
}
|
|
@@ -431,7 +445,8 @@ const TRANSIENT_RETRY_LABELS = {
|
|
|
431
445
|
"unknown": "error",
|
|
432
446
|
};
|
|
433
447
|
async function runAgent(messages, callbacks, channel, signal, options) {
|
|
434
|
-
const
|
|
448
|
+
const facing = (0, channel_1.channelToFacing)(channel);
|
|
449
|
+
const providerRuntime = getProviderRuntime(facing);
|
|
435
450
|
const provider = providerRuntime.id;
|
|
436
451
|
const toolChoiceRequired = options?.toolChoiceRequired ?? true;
|
|
437
452
|
const traceId = options?.traceId;
|
|
@@ -47,6 +47,7 @@ const os = __importStar(require("os"));
|
|
|
47
47
|
const path = __importStar(require("path"));
|
|
48
48
|
const runtime_1 = require("../../nerves/runtime");
|
|
49
49
|
const identity_1 = require("../identity");
|
|
50
|
+
const migrate_config_1 = require("../migrate-config");
|
|
50
51
|
const ANTHROPIC_SETUP_TOKEN_PREFIX = "sk-ant-oat01-";
|
|
51
52
|
const ANTHROPIC_SETUP_TOKEN_MIN_LENGTH = 80;
|
|
52
53
|
const DEFAULT_SECRETS_TEMPLATE = {
|
|
@@ -129,9 +130,22 @@ function readJsonRecord(filePath, label) {
|
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
132
|
function readAgentConfigForAgent(agentName, bundlesRoot = (0, identity_1.getAgentBundlesRoot)()) {
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
|
|
133
|
+
const agentRoot = path.join(bundlesRoot, `${agentName}.ouro`);
|
|
134
|
+
const configPath = path.join(agentRoot, "agent.json");
|
|
135
|
+
let parsed = readJsonRecord(configPath, "agent config");
|
|
136
|
+
// Inline migration: v1 -> v2
|
|
137
|
+
const version = typeof parsed.version === "number" ? parsed.version : 1;
|
|
138
|
+
if (version < 2) {
|
|
139
|
+
(0, migrate_config_1.migrateAgentConfigV1ToV2)(agentRoot);
|
|
140
|
+
parsed = readJsonRecord(configPath, "agent config");
|
|
141
|
+
}
|
|
142
|
+
// Validate v2 required facing fields
|
|
143
|
+
const humanFacing = parsed.humanFacing;
|
|
144
|
+
const agentFacing = parsed.agentFacing;
|
|
145
|
+
if (!humanFacing || typeof humanFacing !== "object") {
|
|
146
|
+
throw new Error(`agent.json at ${configPath} has unsupported provider '${String(parsed.provider)}'`);
|
|
147
|
+
}
|
|
148
|
+
const provider = humanFacing.provider;
|
|
135
149
|
if (provider !== "azure" &&
|
|
136
150
|
provider !== "anthropic" &&
|
|
137
151
|
provider !== "minimax" &&
|
|
@@ -139,20 +153,27 @@ function readAgentConfigForAgent(agentName, bundlesRoot = (0, identity_1.getAgen
|
|
|
139
153
|
provider !== "github-copilot") {
|
|
140
154
|
throw new Error(`agent.json at ${configPath} has unsupported provider '${String(provider)}'`);
|
|
141
155
|
}
|
|
156
|
+
if (!agentFacing || typeof agentFacing !== "object") {
|
|
157
|
+
throw new Error(`agent.json at ${configPath} has unsupported provider '${String(parsed.provider)}'`);
|
|
158
|
+
}
|
|
142
159
|
return {
|
|
143
160
|
configPath,
|
|
144
161
|
config: parsed,
|
|
145
162
|
};
|
|
146
163
|
}
|
|
147
|
-
function writeAgentProviderSelection(agentName, provider, bundlesRoot = (0, identity_1.getAgentBundlesRoot)()) {
|
|
164
|
+
function writeAgentProviderSelection(agentName, facing, provider, bundlesRoot = (0, identity_1.getAgentBundlesRoot)()) {
|
|
148
165
|
const { configPath, config } = readAgentConfigForAgent(agentName, bundlesRoot);
|
|
149
|
-
const
|
|
166
|
+
const facingKey = facing === "human" ? "humanFacing" : "agentFacing";
|
|
167
|
+
const nextConfig = {
|
|
168
|
+
...config,
|
|
169
|
+
[facingKey]: { ...config[facingKey], provider },
|
|
170
|
+
};
|
|
150
171
|
fs.writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
|
|
151
172
|
(0, runtime_1.emitNervesEvent)({
|
|
152
173
|
component: "daemon",
|
|
153
174
|
event: "daemon.auth_provider_selected",
|
|
154
175
|
message: "updated agent provider selection after auth flow",
|
|
155
|
-
meta: { agentName, provider, configPath },
|
|
176
|
+
meta: { agentName, facing, provider, configPath },
|
|
156
177
|
});
|
|
157
178
|
return configPath;
|
|
158
179
|
}
|
|
@@ -189,25 +210,24 @@ function writeProviderCredentials(agentName, provider, credentials, deps = {}) {
|
|
|
189
210
|
writeSecrets(secretsPath, secrets);
|
|
190
211
|
return { secretsPath, secrets };
|
|
191
212
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
return { secretsPath, provider, previousModel };
|
|
213
|
+
function writeAgentModel(agentName, facing, modelName, deps = {}) {
|
|
214
|
+
const { configPath, config } = readAgentConfigForAgent(agentName, deps.bundlesRoot);
|
|
215
|
+
const facingKey = facing === "human" ? "humanFacing" : "agentFacing";
|
|
216
|
+
const facingBlock = config[facingKey];
|
|
217
|
+
const previousModel = facingBlock.model;
|
|
218
|
+
const provider = facingBlock.provider;
|
|
219
|
+
const nextConfig = {
|
|
220
|
+
...config,
|
|
221
|
+
[facingKey]: { ...facingBlock, model: modelName },
|
|
222
|
+
};
|
|
223
|
+
fs.writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
|
|
224
|
+
(0, runtime_1.emitNervesEvent)({
|
|
225
|
+
component: "daemon",
|
|
226
|
+
event: "daemon.config_model_updated",
|
|
227
|
+
message: "updated agent model in agent.json",
|
|
228
|
+
meta: { agentName, facing, provider, modelName, previousModel, configPath },
|
|
229
|
+
});
|
|
230
|
+
return { configPath, provider, previousModel };
|
|
211
231
|
}
|
|
212
232
|
function readCodexAccessToken(homeDir) {
|
|
213
233
|
const authPath = path.join(homeDir, ".codex", "auth.json");
|
|
@@ -65,6 +65,7 @@ const daemon_runtime_sync_1 = require("./daemon-runtime-sync");
|
|
|
65
65
|
const agent_discovery_1 = require("./agent-discovery");
|
|
66
66
|
const update_hooks_1 = require("./update-hooks");
|
|
67
67
|
const bundle_meta_1 = require("./hooks/bundle-meta");
|
|
68
|
+
const agent_config_v2_1 = require("./hooks/agent-config-v2");
|
|
68
69
|
const bundle_manifest_1 = require("../../mind/bundle-manifest");
|
|
69
70
|
const tasks_1 = require("../../repertoire/tasks");
|
|
70
71
|
const thoughts_1 = require("./thoughts");
|
|
@@ -765,12 +766,24 @@ function parseTaskCommand(args) {
|
|
|
765
766
|
}
|
|
766
767
|
throw new Error(`Usage\n${usage()}`);
|
|
767
768
|
}
|
|
769
|
+
function extractFacingFlag(args) {
|
|
770
|
+
const idx = args.indexOf("--facing");
|
|
771
|
+
if (idx === -1 || idx + 1 >= args.length)
|
|
772
|
+
return { rest: args };
|
|
773
|
+
const value = args[idx + 1];
|
|
774
|
+
if (value !== "human" && value !== "agent") {
|
|
775
|
+
throw new Error(`--facing must be 'human' or 'agent'`);
|
|
776
|
+
}
|
|
777
|
+
const rest = [...args.slice(0, idx), ...args.slice(idx + 2)];
|
|
778
|
+
return { facing: value, rest };
|
|
779
|
+
}
|
|
768
780
|
function parseAuthCommand(args) {
|
|
769
781
|
const first = args[0];
|
|
770
782
|
// Support both positional (`auth switch`) and flag (`auth --switch`) forms
|
|
771
783
|
if (first === "verify" || first === "switch" || first === "--verify" || first === "--switch") {
|
|
772
784
|
const subcommand = first.replace(/^--/, "");
|
|
773
|
-
const { agent, rest } = extractAgentFlag(args.slice(1));
|
|
785
|
+
const { agent, rest: afterAgent } = extractAgentFlag(args.slice(1));
|
|
786
|
+
const { facing, rest } = extractFacingFlag(afterAgent);
|
|
774
787
|
let provider;
|
|
775
788
|
/* v8 ignore start -- provider flag parsing: branches tested via CLI parsing tests @preserve */
|
|
776
789
|
for (let i = 0; i < rest.length; i += 1) {
|
|
@@ -790,7 +803,7 @@ function parseAuthCommand(args) {
|
|
|
790
803
|
if (subcommand === "switch") {
|
|
791
804
|
if (!provider)
|
|
792
805
|
throw new Error(`auth switch requires --provider.\n${usage()}`);
|
|
793
|
-
return { kind: "auth.switch", agent, provider };
|
|
806
|
+
return facing ? { kind: "auth.switch", agent, provider, facing } : { kind: "auth.switch", agent, provider };
|
|
794
807
|
}
|
|
795
808
|
return provider ? { kind: "auth.verify", agent, provider } : { kind: "auth.verify", agent };
|
|
796
809
|
}
|
|
@@ -972,7 +985,8 @@ function parseFriendCommand(args) {
|
|
|
972
985
|
throw new Error(`Usage\n${usage()}`);
|
|
973
986
|
}
|
|
974
987
|
function parseConfigCommand(args) {
|
|
975
|
-
const { agent, rest:
|
|
988
|
+
const { agent, rest: afterAgent } = extractAgentFlag(args);
|
|
989
|
+
const { facing, rest: cleaned } = extractFacingFlag(afterAgent);
|
|
976
990
|
const [sub, ...rest] = cleaned;
|
|
977
991
|
if (!sub)
|
|
978
992
|
throw new Error(`Usage\n${usage()}`);
|
|
@@ -982,7 +996,7 @@ function parseConfigCommand(args) {
|
|
|
982
996
|
const modelName = rest[0];
|
|
983
997
|
if (!modelName)
|
|
984
998
|
throw new Error(`Usage: ouro config model --agent <name> <model-name>`);
|
|
985
|
-
return { kind: "config.model", agent, modelName };
|
|
999
|
+
return facing ? { kind: "config.model", agent, modelName, facing } : { kind: "config.model", agent, modelName };
|
|
986
1000
|
}
|
|
987
1001
|
if (sub === "models") {
|
|
988
1002
|
if (!agent)
|
|
@@ -1465,9 +1479,11 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
1465
1479
|
const { loadIdentityPhrases } = await Promise.resolve().then(() => __importStar(require("./specialist-orchestrator")));
|
|
1466
1480
|
const phrases = loadIdentityPhrases(bundleSourceDir, identity.fileName);
|
|
1467
1481
|
setAgentConfigOverride({
|
|
1468
|
-
version:
|
|
1482
|
+
version: 2,
|
|
1469
1483
|
enabled: true,
|
|
1470
1484
|
provider: providerRaw,
|
|
1485
|
+
humanFacing: { provider: providerRaw, model: "" },
|
|
1486
|
+
agentFacing: { provider: providerRaw, model: "" },
|
|
1471
1487
|
phrases,
|
|
1472
1488
|
});
|
|
1473
1489
|
patchRuntimeConfig({
|
|
@@ -1479,6 +1495,7 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
1479
1495
|
const systemPrompt = (0, specialist_prompt_1.buildSpecialistSystemPrompt)(soulText, identity.content, existingBundles, {
|
|
1480
1496
|
tempDir,
|
|
1481
1497
|
provider: providerRaw,
|
|
1498
|
+
model: providerConfig.model ?? "",
|
|
1482
1499
|
});
|
|
1483
1500
|
// Build specialist tools
|
|
1484
1501
|
const specialistTools = (0, specialist_tools_1.getSpecialistTools)();
|
|
@@ -2262,6 +2279,7 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2262
2279
|
}
|
|
2263
2280
|
// Run update hooks before starting daemon so user sees the output
|
|
2264
2281
|
(0, update_hooks_1.registerUpdateHook)(bundle_meta_1.bundleMetaHook);
|
|
2282
|
+
(0, update_hooks_1.registerUpdateHook)(agent_config_v2_1.agentConfigV2Hook);
|
|
2265
2283
|
const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
|
|
2266
2284
|
const currentVersion = (0, bundle_manifest_1.getPackageVersion)();
|
|
2267
2285
|
// Snapshot the previous CLI version from the first bundle-meta before
|
|
@@ -2752,7 +2770,7 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2752
2770
|
}
|
|
2753
2771
|
// ── auth (local, no daemon socket needed) ──
|
|
2754
2772
|
if (command.kind === "auth.run") {
|
|
2755
|
-
const provider = command.provider ?? (0, auth_flow_1.readAgentConfigForAgent)(command.agent).config.provider;
|
|
2773
|
+
const provider = command.provider ?? (0, auth_flow_1.readAgentConfigForAgent)(command.agent).config.humanFacing.provider;
|
|
2756
2774
|
/* v8 ignore next -- tests always inject runAuthFlow; default is for production @preserve */
|
|
2757
2775
|
const authRunner = deps.runAuthFlow ?? auth_flow_1.runRuntimeAuthFlow;
|
|
2758
2776
|
const result = await authRunner({
|
|
@@ -2812,7 +2830,13 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2812
2830
|
deps.writeStdout(message);
|
|
2813
2831
|
return message;
|
|
2814
2832
|
}
|
|
2815
|
-
|
|
2833
|
+
if (command.facing) {
|
|
2834
|
+
(0, auth_flow_1.writeAgentProviderSelection)(command.agent, command.facing, command.provider);
|
|
2835
|
+
}
|
|
2836
|
+
else {
|
|
2837
|
+
(0, auth_flow_1.writeAgentProviderSelection)(command.agent, "human", command.provider);
|
|
2838
|
+
(0, auth_flow_1.writeAgentProviderSelection)(command.agent, "agent", command.provider);
|
|
2839
|
+
}
|
|
2816
2840
|
const message = `switched ${command.agent} to ${command.provider} (verified working)`;
|
|
2817
2841
|
deps.writeStdout(message);
|
|
2818
2842
|
return message;
|
|
@@ -2822,7 +2846,7 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2822
2846
|
/* v8 ignore start -- config models: tested via daemon-cli.test.ts @preserve */
|
|
2823
2847
|
if (command.kind === "config.models") {
|
|
2824
2848
|
const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent);
|
|
2825
|
-
const provider = config.provider;
|
|
2849
|
+
const provider = config.humanFacing.provider;
|
|
2826
2850
|
if (provider !== "github-copilot") {
|
|
2827
2851
|
const message = `model listing not available for ${provider} — check provider documentation.`;
|
|
2828
2852
|
deps.writeStdout(message);
|
|
@@ -2853,9 +2877,11 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2853
2877
|
// ── config model (local, no daemon socket needed) ──
|
|
2854
2878
|
/* v8 ignore start -- config model: tested via daemon-cli.test.ts @preserve */
|
|
2855
2879
|
if (command.kind === "config.model") {
|
|
2880
|
+
const facing = command.facing ?? "human";
|
|
2856
2881
|
// Validate model availability for github-copilot before writing
|
|
2857
2882
|
const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent);
|
|
2858
|
-
|
|
2883
|
+
const facingConfig = facing === "human" ? config.humanFacing : config.agentFacing;
|
|
2884
|
+
if (facingConfig.provider === "github-copilot") {
|
|
2859
2885
|
const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent);
|
|
2860
2886
|
const ghConfig = secrets.providers["github-copilot"];
|
|
2861
2887
|
if (ghConfig.githubToken && ghConfig.baseUrl) {
|
|
@@ -2881,7 +2907,7 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2881
2907
|
}
|
|
2882
2908
|
}
|
|
2883
2909
|
}
|
|
2884
|
-
const { provider, previousModel } = (0, auth_flow_1.writeAgentModel)(command.agent, command.modelName);
|
|
2910
|
+
const { provider, previousModel } = (0, auth_flow_1.writeAgentModel)(command.agent, facing, command.modelName);
|
|
2885
2911
|
const message = previousModel
|
|
2886
2912
|
? `updated ${command.agent} model on ${provider}: ${previousModel} → ${command.modelName}`
|
|
2887
2913
|
: `set ${command.agent} model on ${provider}: ${command.modelName}`;
|
|
@@ -46,6 +46,7 @@ const runtime_metadata_1 = require("./runtime-metadata");
|
|
|
46
46
|
const runtime_mode_1 = require("./runtime-mode");
|
|
47
47
|
const update_hooks_1 = require("./update-hooks");
|
|
48
48
|
const bundle_meta_1 = require("./hooks/bundle-meta");
|
|
49
|
+
const agent_config_v2_1 = require("./hooks/agent-config-v2");
|
|
49
50
|
const bundle_manifest_1 = require("../../mind/bundle-manifest");
|
|
50
51
|
const update_checker_1 = require("./update-checker");
|
|
51
52
|
const staged_restart_1 = require("./staged-restart");
|
|
@@ -200,6 +201,7 @@ class OuroDaemon {
|
|
|
200
201
|
});
|
|
201
202
|
// Register update hooks and apply pending updates before starting agents
|
|
202
203
|
(0, update_hooks_1.registerUpdateHook)(bundle_meta_1.bundleMetaHook);
|
|
204
|
+
(0, update_hooks_1.registerUpdateHook)(agent_config_v2_1.agentConfigV2Hook);
|
|
203
205
|
const currentVersion = (0, bundle_manifest_1.getPackageVersion)();
|
|
204
206
|
await (0, update_hooks_1.applyPendingUpdates)(this.bundlesRoot, currentVersion);
|
|
205
207
|
// Start periodic update checker (polls npm registry every 30 minutes)
|
|
@@ -134,6 +134,8 @@ function writeDiaryScaffold(bundleRoot) {
|
|
|
134
134
|
function writeHatchlingAgentConfig(bundleRoot, input) {
|
|
135
135
|
const template = (0, identity_1.buildDefaultAgentTemplate)(input.agentName);
|
|
136
136
|
template.provider = input.provider;
|
|
137
|
+
template.humanFacing = { provider: input.provider, model: template.humanFacing.model };
|
|
138
|
+
template.agentFacing = { provider: input.provider, model: template.agentFacing.model };
|
|
137
139
|
template.enabled = true;
|
|
138
140
|
fs.writeFileSync(path.join(bundleRoot, "agent.json"), `${JSON.stringify(template, null, 2)}\n`, "utf-8");
|
|
139
141
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.agentConfigV2Hook = agentConfigV2Hook;
|
|
4
|
+
const runtime_1 = require("../../../nerves/runtime");
|
|
5
|
+
const migrate_config_1 = require("../../migrate-config");
|
|
6
|
+
async function agentConfigV2Hook(ctx) {
|
|
7
|
+
(0, runtime_1.emitNervesEvent)({
|
|
8
|
+
component: "daemon",
|
|
9
|
+
event: "daemon.agent_config_v2_hook_start",
|
|
10
|
+
message: "running agent-config-v2 update hook",
|
|
11
|
+
meta: { agentRoot: ctx.agentRoot, currentVersion: ctx.currentVersion },
|
|
12
|
+
});
|
|
13
|
+
try {
|
|
14
|
+
(0, migrate_config_1.migrateAgentConfigV1ToV2)(ctx.agentRoot);
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
const errorMessage = err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err);
|
|
18
|
+
(0, runtime_1.emitNervesEvent)({
|
|
19
|
+
component: "daemon",
|
|
20
|
+
event: "daemon.agent_config_v2_hook_error",
|
|
21
|
+
message: "agent-config-v2 hook migration failed",
|
|
22
|
+
meta: { agentRoot: ctx.agentRoot, error: errorMessage },
|
|
23
|
+
});
|
|
24
|
+
return { ok: false, error: errorMessage };
|
|
25
|
+
}
|
|
26
|
+
(0, runtime_1.emitNervesEvent)({
|
|
27
|
+
component: "daemon",
|
|
28
|
+
event: "daemon.agent_config_v2_hook_end",
|
|
29
|
+
message: "agent-config-v2 hook completed",
|
|
30
|
+
meta: { agentRoot: ctx.agentRoot },
|
|
31
|
+
});
|
|
32
|
+
return { ok: true };
|
|
33
|
+
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.runHooks = runHooks;
|
|
4
4
|
const runtime_1 = require("../../nerves/runtime");
|
|
5
5
|
const bundle_meta_1 = require("./hooks/bundle-meta");
|
|
6
|
+
const agent_config_v2_1 = require("./hooks/agent-config-v2");
|
|
6
7
|
async function runHooks(deps) {
|
|
7
8
|
(0, runtime_1.emitNervesEvent)({
|
|
8
9
|
component: "daemon",
|
|
@@ -12,6 +13,7 @@ async function runHooks(deps) {
|
|
|
12
13
|
});
|
|
13
14
|
try {
|
|
14
15
|
deps.registerUpdateHook(bundle_meta_1.bundleMetaHook);
|
|
16
|
+
deps.registerUpdateHook(agent_config_v2_1.agentConfigV2Hook);
|
|
15
17
|
const currentVersion = deps.getPackageVersion();
|
|
16
18
|
await deps.applyPendingUpdates(deps.bundlesRoot, currentVersion);
|
|
17
19
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -59,8 +59,10 @@ function buildSpecialistSystemPrompt(soulText, identityText, existingBundles, co
|
|
|
59
59
|
"It also needs an **agent.json** with at minimum:",
|
|
60
60
|
'```json',
|
|
61
61
|
'{',
|
|
62
|
+
' "version": 2,',
|
|
62
63
|
' "name": "AgentName",',
|
|
63
|
-
` "provider": "${context.provider}",`,
|
|
64
|
+
` "humanFacing": { "provider": "${context.provider}", "model": "${context.model}" },`,
|
|
65
|
+
` "agentFacing": { "provider": "${context.provider}", "model": "${context.model}" },`,
|
|
64
66
|
' "enabled": true',
|
|
65
67
|
'}',
|
|
66
68
|
'```',
|