@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.
@@ -1,7 +1,8 @@
1
1
  {
2
- "version": 1,
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 explicit
115
- `--provider` form is for adding or switching providers, and it updates `agent.json`
116
- to use the newly authenticated provider.
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": [
@@ -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
  },
@@ -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
- let _providerRuntime = null;
37
- function getProviderRuntimeFingerprint() {
38
- const provider = (0, identity_1.loadAgentConfig)().provider;
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, modelName, apiVersion, managedIdentityClientId } = (0, config_1.getAzureConfig)();
43
- return JSON.stringify({ provider, apiKey, endpoint, deployment, modelName, apiVersion, managedIdentityClientId });
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 { model, setupToken } = (0, config_1.getAnthropicConfig)();
52
+ const { setupToken } = (0, config_1.getAnthropicConfig)();
47
53
  return JSON.stringify({ provider, model, setupToken });
48
54
  }
49
55
  case "minimax": {
50
- const { apiKey, model } = (0, config_1.getMinimaxConfig)();
51
- return JSON.stringify({ provider, apiKey, model });
56
+ const { apiKey } = (0, config_1.getMinimaxConfig)();
57
+ return JSON.stringify({ provider, model, apiKey });
52
58
  }
53
59
  case "openai-codex": {
54
- const { model, oauthAccessToken } = (0, config_1.getOpenAICodexConfig)();
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 { model, githubToken, baseUrl } = (0, config_1.getGithubCopilotConfig)();
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 provider = (0, identity_1.loadAgentConfig)().provider;
76
- return factories[provider]();
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
- if (!_providerRuntime || _providerRuntime.fingerprint !== fingerprint) {
84
- const runtime = createProviderRegistry().resolve();
85
- _providerRuntime = runtime ? { fingerprint, runtime } : null;
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 (!_providerRuntime) {
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 _providerRuntime.runtime;
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
- _providerRuntime = null;
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 provider = (0, identity_1.loadAgentConfig)().provider;
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 config = (0, config_1.getAzureConfig)();
149
- return `azure openai (${config.deployment || "default"}, model: ${config.modelName || "unknown"})`;
162
+ const azureCfg = (0, config_1.getAzureConfig)();
163
+ return `azure openai (${azureCfg.deployment || "default"}, model: ${model})`;
150
164
  },
151
- anthropic: () => `anthropic (${(0, config_1.getAnthropicConfig)().model || "unknown"})`,
152
- minimax: () => `minimax (${(0, config_1.getMinimaxConfig)().model || "unknown"})`,
153
- "openai-codex": () => `openai codex (${(0, config_1.getOpenAICodexConfig)().model || "unknown"})`,
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 (${(0, config_1.getGithubCopilotConfig)().model || "unknown"})`,
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 providerRuntime = getProviderRuntime();
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 configPath = path.join(bundlesRoot, `${agentName}.ouro`, "agent.json");
133
- const parsed = readJsonRecord(configPath, "agent config");
134
- const provider = parsed.provider;
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 nextConfig = { ...config, provider };
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
- const MODEL_FIELD = {
193
- azure: "modelName",
194
- minimax: "model",
195
- anthropic: "model",
196
- "openai-codex": "model",
197
- "github-copilot": "model",
198
- };
199
- function writeAgentModel(agentName, modelName, deps = {}) {
200
- const { config } = readAgentConfigForAgent(agentName, deps.bundlesRoot);
201
- const provider = config.provider;
202
- const { secretsPath, secrets } = loadAgentSecrets(agentName, deps);
203
- const providerSecrets = secrets.providers[provider];
204
- /* v8 ignore next -- fallback: all known providers are in MODEL_FIELD @preserve */
205
- const fieldName = MODEL_FIELD[provider] ?? "model";
206
- /* v8 ignore next -- defensive: fieldName always exists in template @preserve */
207
- const previousModel = providerSecrets[fieldName] ?? "";
208
- providerSecrets[fieldName] = modelName;
209
- writeSecrets(secretsPath, secrets);
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: cleaned } = extractAgentFlag(args);
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: 1,
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
- (0, auth_flow_1.writeAgentProviderSelection)(command.agent, command.provider);
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
- if (config.provider === "github-copilot") {
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
  '```',