@ouro.bot/cli 0.1.0-alpha.363 → 0.1.0-alpha.365

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.
Files changed (44) hide show
  1. package/README.md +16 -7
  2. package/changelog.json +17 -0
  3. package/dist/heart/auth/auth-flow.js +25 -110
  4. package/dist/heart/config.js +69 -55
  5. package/dist/heart/core.js +83 -33
  6. package/dist/heart/daemon/agent-config-check.js +41 -238
  7. package/dist/heart/daemon/agentic-repair.js +1 -1
  8. package/dist/heart/daemon/cli-defaults.js +15 -68
  9. package/dist/heart/daemon/cli-exec.js +334 -102
  10. package/dist/heart/daemon/cli-parse.js +71 -0
  11. package/dist/heart/daemon/daemon-cli.js +1 -2
  12. package/dist/heart/daemon/daemon-entry.js +1 -3
  13. package/dist/heart/daemon/doctor.js +9 -29
  14. package/dist/heart/daemon/provider-discovery.js +32 -59
  15. package/dist/heart/hatch/hatch-flow.js +9 -12
  16. package/dist/heart/hatch/specialist-prompt.js +1 -1
  17. package/dist/heart/hatch/specialist-tools.js +21 -1
  18. package/dist/heart/migrate-config.js +15 -42
  19. package/dist/heart/provider-binding-resolver.js +6 -7
  20. package/dist/heart/provider-credentials.js +379 -0
  21. package/dist/heart/provider-failover.js +3 -11
  22. package/dist/heart/provider-ping.js +13 -3
  23. package/dist/heart/provider-state.js +8 -0
  24. package/dist/heart/provider-visibility.js +3 -6
  25. package/dist/heart/providers/anthropic-token.js +15 -47
  26. package/dist/heart/providers/anthropic.js +4 -9
  27. package/dist/heart/providers/azure.js +3 -3
  28. package/dist/heart/providers/github-copilot.js +2 -2
  29. package/dist/heart/providers/minimax-vlm.js +2 -2
  30. package/dist/heart/providers/minimax.js +1 -1
  31. package/dist/heart/providers/openai-codex.js +4 -9
  32. package/dist/heart/versioning/ouro-path-installer.js +10 -5
  33. package/dist/mind/prompt.js +1 -1
  34. package/dist/repertoire/bitwarden-store.js +63 -17
  35. package/dist/repertoire/bundle-templates.js +2 -2
  36. package/dist/repertoire/credential-access.js +47 -467
  37. package/dist/repertoire/tools-attachments.js +5 -4
  38. package/dist/repertoire/tools-vault.js +10 -80
  39. package/dist/repertoire/vault-unlock.js +359 -0
  40. package/dist/senses/bluebubbles/client.js +39 -4
  41. package/dist/senses/pipeline.js +0 -1
  42. package/package.json +1 -1
  43. package/skills/configure-dev-tools.md +10 -0
  44. package/dist/heart/provider-credential-pool.js +0 -395
@@ -65,6 +65,7 @@ function usage() {
65
65
  " ouro status --agent <name>",
66
66
  " ouro use --agent <name> --lane outward|inner --provider <provider> --model <model> [--force]",
67
67
  " ouro check --agent <name> --lane outward|inner",
68
+ " ouro provider refresh --agent <name>",
68
69
  " ouro outlook [--json]",
69
70
  " ouro -v|--version",
70
71
  " ouro config model --agent <name> <model-name>",
@@ -72,6 +73,9 @@ function usage() {
72
73
  " ouro auth --agent <name> [--provider <provider>]",
73
74
  " ouro auth verify --agent <name> [--provider <provider>]",
74
75
  " ouro auth switch --agent <name> --provider <provider>",
76
+ " ouro vault create --agent <name> --email <email> [--server <url>] [--store <store>] [--generate-unlock-secret]",
77
+ " ouro vault unlock --agent <name> [--store auto|macos-keychain|windows-dpapi|linux-secret-service|plaintext-file]",
78
+ " ouro vault status --agent <name> [--store auto|macos-keychain|windows-dpapi|linux-secret-service|plaintext-file]",
75
79
  " ouro chat <agent>",
76
80
  " ouro msg --to <agent> [--session <id>] [--task <ref>] <message>",
77
81
  " ouro poke <agent> --task <task-id>",
@@ -426,6 +430,61 @@ function parseAuthCommand(args) {
426
430
  }
427
431
  return provider ? { kind: "auth.run", agent, provider } : { kind: "auth.run", agent };
428
432
  }
433
+ function isVaultUnlockStoreKind(value) {
434
+ return value === "auto" || value === "macos-keychain" || value === "windows-dpapi" || value === "linux-secret-service" || value === "plaintext-file";
435
+ }
436
+ function parseVaultCommand(args) {
437
+ const sub = args[0];
438
+ const { agent, rest } = extractAgentFlag(args.slice(1));
439
+ let email;
440
+ let serverUrl;
441
+ let store;
442
+ let generateUnlockSecret = false;
443
+ for (let i = 0; i < rest.length; i += 1) {
444
+ const token = rest[i];
445
+ if (token === "--email") {
446
+ email = rest[i + 1];
447
+ i += 1;
448
+ continue;
449
+ }
450
+ if (token === "--server") {
451
+ serverUrl = rest[i + 1];
452
+ i += 1;
453
+ continue;
454
+ }
455
+ if (token === "--store") {
456
+ const value = rest[i + 1];
457
+ if (!isVaultUnlockStoreKind(value)) {
458
+ throw new Error("vault --store must be auto|macos-keychain|windows-dpapi|linux-secret-service|plaintext-file");
459
+ }
460
+ store = value;
461
+ i += 1;
462
+ continue;
463
+ }
464
+ if (token === "--generate-unlock-secret") {
465
+ generateUnlockSecret = true;
466
+ continue;
467
+ }
468
+ throw new Error("Usage: ouro vault create|unlock|status --agent <name>");
469
+ }
470
+ if (!agent || (sub !== "create" && sub !== "unlock" && sub !== "status")) {
471
+ throw new Error("Usage: ouro vault create|unlock|status --agent <name>");
472
+ }
473
+ if (sub === "create") {
474
+ return {
475
+ kind: "vault.create",
476
+ agent,
477
+ ...(email ? { email } : {}),
478
+ ...(serverUrl ? { serverUrl } : {}),
479
+ ...(store ? { store } : {}),
480
+ ...(generateUnlockSecret ? { generateUnlockSecret: true } : {}),
481
+ };
482
+ }
483
+ if (sub === "unlock") {
484
+ return { kind: "vault.unlock", agent, ...(store ? { store } : {}) };
485
+ }
486
+ return { kind: "vault.status", agent, ...(store ? { store } : {}) };
487
+ }
429
488
  function parseProviderUseCommand(args) {
430
489
  const { agent, rest: afterAgent } = extractAgentFlag(args);
431
490
  const { facing, rest: afterFacing } = extractFacingFlag(afterAgent);
@@ -483,6 +542,14 @@ function parseProviderCheckCommand(args) {
483
542
  ...(facing ? { legacyFacing: facing } : {}),
484
543
  };
485
544
  }
545
+ function parseProviderCommand(args) {
546
+ const sub = args[0];
547
+ const { agent, rest } = extractAgentFlag(args.slice(1));
548
+ if (sub === "refresh" && agent && rest.length === 0) {
549
+ return { kind: "provider.refresh", agent };
550
+ }
551
+ throw new Error("Usage: ouro provider refresh --agent <name>");
552
+ }
486
553
  function parseReminderCommand(args) {
487
554
  const { agent, rest: cleaned } = extractAgentFlag(args);
488
555
  const [sub, ...rest] = cleaned;
@@ -878,6 +945,8 @@ function parseOuroCommand(args) {
878
945
  return parseProviderUseCommand(args.slice(1));
879
946
  if (head === "check")
880
947
  return parseProviderCheckCommand(args.slice(1));
948
+ if (head === "provider")
949
+ return parseProviderCommand(args.slice(1));
881
950
  if (head === "logs") {
882
951
  if (second === "prune")
883
952
  return { kind: "daemon.logs.prune" };
@@ -889,6 +958,8 @@ function parseOuroCommand(args) {
889
958
  return parseHatchCommand(args.slice(1));
890
959
  if (head === "auth")
891
960
  return parseAuthCommand(args.slice(1));
961
+ if (head === "vault")
962
+ return parseVaultCommand(args.slice(1));
892
963
  if (head === "task")
893
964
  return parseTaskCommand(args.slice(1));
894
965
  if (head === "reminder")
@@ -14,7 +14,7 @@
14
14
  * ouro-entry.ts, ouro-bot-wrapper.ts) continue to work unchanged.
15
15
  */
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.discoverExistingCredentials = exports.readFirstBundleMetaVersion = exports.createDefaultOuroCliDeps = exports.pingGithubCopilotModel = exports.listGithubCopilotModels = exports.ensureDaemonRunning = exports.runOuroCli = exports.parseOuroCommand = void 0;
17
+ exports.readFirstBundleMetaVersion = exports.createDefaultOuroCliDeps = exports.pingGithubCopilotModel = exports.listGithubCopilotModels = exports.ensureDaemonRunning = exports.runOuroCli = exports.parseOuroCommand = void 0;
18
18
  // ── Parsing ──
19
19
  var cli_parse_1 = require("./cli-parse");
20
20
  Object.defineProperty(exports, "parseOuroCommand", { enumerable: true, get: function () { return cli_parse_1.parseOuroCommand; } });
@@ -29,4 +29,3 @@ Object.defineProperty(exports, "pingGithubCopilotModel", { enumerable: true, get
29
29
  var cli_defaults_1 = require("./cli-defaults");
30
30
  Object.defineProperty(exports, "createDefaultOuroCliDeps", { enumerable: true, get: function () { return cli_defaults_1.createDefaultOuroCliDeps; } });
31
31
  Object.defineProperty(exports, "readFirstBundleMetaVersion", { enumerable: true, get: function () { return cli_defaults_1.readFirstBundleMetaVersion; } });
32
- Object.defineProperty(exports, "discoverExistingCredentials", { enumerable: true, get: function () { return cli_defaults_1.discoverExistingCredentials; } });
@@ -54,7 +54,6 @@ const habit_migration_1 = require("../habits/habit-migration");
54
54
  const os_cron_deps_1 = require("./os-cron-deps");
55
55
  const os_cron_1 = require("./os-cron");
56
56
  const daemon_tombstone_1 = require("./daemon-tombstone");
57
- const os = __importStar(require("os"));
58
57
  const agent_config_check_1 = require("./agent-config-check");
59
58
  const pulse_1 = require("./pulse");
60
59
  const socket_client_1 = require("./socket-client");
@@ -102,8 +101,7 @@ const processManager = new process_manager_1.DaemonProcessManager({
102
101
  /* v8 ignore next 4 -- wiring: delegates to checkAgentConfigWithProviderHealth which has full unit tests @preserve */
103
102
  configCheck: async (agent) => {
104
103
  const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
105
- const secretsRoot = path.join(os.homedir(), ".agentsecrets");
106
- return (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agent, bundlesRoot, secretsRoot);
104
+ return (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agent, bundlesRoot);
107
105
  },
108
106
  /* v8 ignore start -- pulse flush wiring: integration code; flushPulse itself has full unit tests @preserve */
109
107
  onSnapshotChange: () => {
@@ -300,38 +300,18 @@ function checkHabits(deps) {
300
300
  function checkSecurity(deps) {
301
301
  const checks = [];
302
302
  const agents = discoverAgents(deps);
303
- const providerPoolPath = `${deps.secretsRoot}/providers.json`;
304
- if (deps.existsSync(providerPoolPath)) {
305
- const stat = deps.statSync(providerPoolPath);
306
- const worldReadable = (stat.mode & 0o004) !== 0;
307
- checks.push({
308
- label: "machine provider credentials perms",
309
- status: worldReadable ? "warn" : "pass",
310
- detail: worldReadable ? "world-readable — consider chmod 600" : "not world-readable",
311
- });
312
- try {
313
- JSON.parse(deps.readFileSync(providerPoolPath));
314
- checks.push({ label: "machine provider credentials", status: "pass", detail: "readable JSON" });
315
- }
316
- catch {
317
- checks.push({ label: "machine provider credentials", status: "fail", detail: "unparseable JSON" });
318
- }
319
- }
320
303
  for (const agentDir of agents) {
321
304
  const agentName = agentDir.replace(/\.ouro$/, "");
322
305
  const secretsPath = `${deps.secretsRoot}/${agentName}/secrets.json`;
323
- if (!deps.existsSync(secretsPath)) {
324
- checks.push({ label: `${agentDir} secrets.json`, status: "fail", detail: "missing" });
325
- continue;
326
- }
327
- // Check file permissions
328
- const stat = deps.statSync(secretsPath);
329
- const worldReadable = (stat.mode & 0o004) !== 0;
330
- if (worldReadable) {
331
- checks.push({ label: `${agentDir} secrets.json perms`, status: "warn", detail: "world-readable — consider chmod 600" });
332
- }
333
- else {
334
- checks.push({ label: `${agentDir} secrets.json perms`, status: "pass", detail: "not world-readable" });
306
+ if (deps.existsSync(secretsPath)) {
307
+ const stat = deps.statSync(secretsPath);
308
+ const worldReadable = (stat.mode & 0o004) !== 0;
309
+ if (worldReadable) {
310
+ checks.push({ label: `${agentDir} legacy secrets.json perms`, status: "warn", detail: "world-readable — consider deleting or chmod 600" });
311
+ }
312
+ else {
313
+ checks.push({ label: `${agentDir} legacy secrets.json perms`, status: "pass", detail: "not world-readable" });
314
+ }
335
315
  }
336
316
  // Check agent.json for leaked credential keys
337
317
  const configPath = `${deps.bundlesRoot}/${agentDir}/agent.json`;
@@ -1,19 +1,20 @@
1
1
  "use strict";
2
2
  /**
3
- * Shared provider discovery — single path for finding a working LLM provider.
3
+ * Shared provider discovery for repair.
4
4
  *
5
- * Scans machine-pool credentials, legacy disk credentials, and environment variables,
6
- * deduplicates by provider, then pings each candidate to validate credentials actually work.
5
+ * Runtime repair only trusts the agent vault. First-run conveniences may still
6
+ * inspect env vars before credentials are stored, but once an agent exists the
7
+ * vault is the source of truth.
7
8
  */
8
9
  Object.defineProperty(exports, "__esModule", { value: true });
9
10
  exports.scanEnvVarCredentials = scanEnvVarCredentials;
10
11
  exports.discoverWorkingProvider = discoverWorkingProvider;
11
12
  const identity_1 = require("../identity");
12
- const provider_credential_pool_1 = require("../provider-credential-pool");
13
+ const provider_credentials_1 = require("../provider-credentials");
13
14
  const runtime_1 = require("../../nerves/runtime");
14
15
  /**
15
- * Scan environment variables for API keys using the canonical PROVIDER_CREDENTIALS descriptor.
16
- * Returns one DiscoveredCredential per provider that has at least one required field set.
16
+ * Scan environment variables for API keys during first-run bootstrap.
17
+ * This does not participate in runtime provider repair.
17
18
  */
18
19
  function scanEnvVarCredentials(env) {
19
20
  const results = [];
@@ -25,7 +26,6 @@ function scanEnvVarCredentials(env) {
25
26
  cred[credKey] = value;
26
27
  }
27
28
  }
28
- // Only register if at least one required field was found
29
29
  const hasRequired = desc.required.some((key) => !!cred[key]);
30
30
  if (hasRequired) {
31
31
  results.push({
@@ -45,62 +45,35 @@ function stringifyProviderFields(fields) {
45
45
  }
46
46
  return result;
47
47
  }
48
- function machinePoolSource(record) {
49
- if (record.provenance.contributedByAgent) {
50
- return `machine-pool:${record.provenance.contributedByAgent}`;
51
- }
52
- return `machine-pool:${record.provenance.source}`;
53
- }
54
- function discoverMachinePoolCredentials(secretsRoot) {
55
- const homeDir = (0, provider_credential_pool_1.providerCredentialHomeDirFromSecretsRoot)(secretsRoot);
56
- const poolResult = (0, provider_credential_pool_1.readProviderCredentialPool)(homeDir);
57
- if (!poolResult.ok)
58
- return [];
59
- const credentials = [];
60
- for (const [, record] of Object.entries(poolResult.pool.providers)) {
61
- credentials.push({
62
- provider: record.provider,
63
- agentName: machinePoolSource(record),
64
- credentials: stringifyProviderFields(record.credentials),
65
- providerConfig: stringifyProviderFields(record.config),
66
- });
67
- }
68
- return credentials;
48
+ function discoveredFromVaultRecord(record) {
49
+ return {
50
+ provider: record.provider,
51
+ agentName: "vault",
52
+ credentials: stringifyProviderFields(record.credentials),
53
+ providerConfig: stringifyProviderFields(record.config),
54
+ };
69
55
  }
70
- /**
71
- * Discover the first working provider by scanning configured credential sources,
72
- * deduplicating by provider, and pinging each candidate.
73
- */
74
56
  async function discoverWorkingProvider(deps) {
75
- const poolCreds = discoverMachinePoolCredentials(deps.secretsRoot);
76
- const diskCreds = deps.discoverExistingCredentials(deps.secretsRoot);
77
- const envCreds = scanEnvVarCredentials(deps.env);
78
- // Deduplicate: machine pool first, legacy per-agent disk next, env vars last.
79
- const seenProviders = new Set();
80
- const candidates = [];
81
- for (const cred of poolCreds) {
82
- seenProviders.add(cred.provider);
83
- candidates.push(cred);
84
- }
85
- for (const cred of diskCreds) {
86
- if (!seenProviders.has(cred.provider)) {
87
- seenProviders.add(cred.provider);
88
- candidates.push(cred);
89
- }
90
- }
91
- for (const cred of envCreds) {
92
- if (!seenProviders.has(cred.provider)) {
93
- seenProviders.add(cred.provider);
94
- candidates.push(cred);
95
- }
57
+ const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(deps.agentName);
58
+ if (!poolResult.ok) {
59
+ (0, runtime_1.emitNervesEvent)({
60
+ level: "warn",
61
+ component: "daemon",
62
+ event: "daemon.provider_discovery_none",
63
+ message: "provider discovery could not read agent vault",
64
+ meta: { agentName: deps.agentName, reason: poolResult.reason },
65
+ });
66
+ return null;
96
67
  }
68
+ const candidates = Object.entries(poolResult.pool.providers)
69
+ .map(([, record]) => discoveredFromVaultRecord(record));
97
70
  if (candidates.length === 0) {
98
71
  (0, runtime_1.emitNervesEvent)({
99
72
  level: "info",
100
73
  component: "daemon",
101
74
  event: "daemon.provider_discovery_none",
102
- message: "no provider credentials found in machine pool, legacy disk, or environment",
103
- meta: {},
75
+ message: "no provider credentials found in agent vault",
76
+ meta: { agentName: deps.agentName },
104
77
  });
105
78
  return null;
106
79
  }
@@ -111,7 +84,7 @@ async function discoverWorkingProvider(deps) {
111
84
  component: "daemon",
112
85
  event: "daemon.provider_discovery_ping",
113
86
  message: `pinging provider: ${candidate.provider}`,
114
- meta: { provider: candidate.provider, source: candidate.agentName },
87
+ meta: { agentName: deps.agentName, provider: candidate.provider, source: candidate.agentName },
115
88
  });
116
89
  const result = await deps.pingProvider(candidate.provider, config);
117
90
  if (result.ok) {
@@ -120,7 +93,7 @@ async function discoverWorkingProvider(deps) {
120
93
  component: "daemon",
121
94
  event: "daemon.provider_discovery_ok",
122
95
  message: `provider discovery succeeded: ${candidate.provider}`,
123
- meta: { provider: candidate.provider, source: candidate.agentName },
96
+ meta: { agentName: deps.agentName, provider: candidate.provider, source: candidate.agentName },
124
97
  });
125
98
  return {
126
99
  provider: candidate.provider,
@@ -133,8 +106,8 @@ async function discoverWorkingProvider(deps) {
133
106
  level: "warn",
134
107
  component: "daemon",
135
108
  event: "daemon.provider_discovery_all_failed",
136
- message: "all provider candidates failed ping",
137
- meta: { candidateCount: candidates.length },
109
+ message: "all vault provider candidates failed ping",
110
+ meta: { agentName: deps.agentName, candidateCount: candidates.length },
138
111
  });
139
112
  return null;
140
113
  }
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.writeSecretsFile = writeSecretsFile;
36
+ exports.storeHatchlingProviderCredentials = storeHatchlingProviderCredentials;
37
37
  exports.runHatchFlow = runHatchFlow;
38
38
  const fs = __importStar(require("fs"));
39
39
  const os = __importStar(require("os"));
@@ -45,7 +45,7 @@ const auth_flow_1 = require("../auth/auth-flow");
45
45
  const provider_models_1 = require("../provider-models");
46
46
  const habit_parser_1 = require("../habits/habit-parser");
47
47
  const machine_identity_1 = require("../machine-identity");
48
- const provider_credential_pool_1 = require("../provider-credential-pool");
48
+ const provider_credentials_1 = require("../provider-credentials");
49
49
  const provider_state_1 = require("../provider-state");
50
50
  const hatch_specialist_1 = require("./hatch-specialist");
51
51
  function requiredCredentialKeys(provider) {
@@ -67,8 +67,8 @@ function validateCredentials(provider, credentials) {
67
67
  throw new Error(`Missing required credentials for ${provider}: ${missing.join(", ")}`);
68
68
  }
69
69
  }
70
- function writeSecretsFile(agentName, provider, credentials, secretsRoot) {
71
- return (0, auth_flow_1.writeProviderCredentials)(agentName, provider, credentials, { secretsRoot }).secretsPath;
70
+ async function storeHatchlingProviderCredentials(agentName, provider, credentials) {
71
+ return (await (0, auth_flow_1.storeProviderCredentials)(agentName, provider, credentials)).credentialPath;
72
72
  }
73
73
  function writeReadme(dir, purpose) {
74
74
  fs.mkdirSync(dir, { recursive: true });
@@ -135,10 +135,10 @@ function writeHatchlingAgentConfig(bundleRoot, input) {
135
135
  template.enabled = true;
136
136
  fs.writeFileSync(path.join(bundleRoot, "agent.json"), `${JSON.stringify(template, null, 2)}\n`, "utf-8");
137
137
  }
138
- function writeHatchlingProviderState(bundleRoot, input, secretsRoot, now) {
138
+ function writeHatchlingProviderState(bundleRoot, input, now) {
139
139
  const model = (0, provider_models_1.getDefaultModelForProvider)(input.provider);
140
140
  const machine = (0, machine_identity_1.loadOrCreateMachineIdentity)({
141
- homeDir: (0, provider_credential_pool_1.providerCredentialHomeDirFromSecretsRoot)(secretsRoot),
141
+ homeDir: (0, provider_credentials_1.providerCredentialMachineHomeDir)(),
142
142
  now: () => now,
143
143
  });
144
144
  const state = (0, provider_state_1.bootstrapProviderStateFromAgentConfig)({
@@ -160,7 +160,6 @@ async function runHatchFlow(input, deps = {}) {
160
160
  });
161
161
  validateCredentials(input.provider, input.credentials);
162
162
  const bundlesRoot = deps.bundlesRoot ?? path.join(os.homedir(), "AgentBundles");
163
- const secretsRoot = deps.secretsRoot ?? path.join(os.homedir(), ".agentsecrets");
164
163
  const sourceIdentities = deps.specialistIdentitySourceDir ?? (0, hatch_specialist_1.getSpecialistIdentitySourceDir)();
165
164
  const targetIdentities = deps.specialistIdentityTargetDir ?? (0, hatch_specialist_1.getRepoSpecialistIdentitiesDir)();
166
165
  const now = deps.now ? deps.now() : new Date();
@@ -173,8 +172,6 @@ async function runHatchFlow(input, deps = {}) {
173
172
  identitiesDir: targetIdentities,
174
173
  random,
175
174
  });
176
- const specialistSecretsPath = writeSecretsFile("SerpentGuide", input.provider, input.credentials, secretsRoot);
177
- const hatchlingSecretsPath = writeSecretsFile(input.agentName, input.provider, input.credentials, secretsRoot);
178
175
  const bundleRoot = path.join(bundlesRoot, `${input.agentName}.ouro`);
179
176
  fs.mkdirSync(bundleRoot, { recursive: true });
180
177
  writeReadme(bundleRoot, "Root of this agent bundle.");
@@ -189,7 +186,8 @@ async function runHatchFlow(input, deps = {}) {
189
186
  writeReadme(path.join(bundleRoot, "senses"), "Sense-specific config.");
190
187
  writeReadme(path.join(bundleRoot, "senses", "teams"), "Teams sense config.");
191
188
  writeHatchlingAgentConfig(bundleRoot, input);
192
- writeHatchlingProviderState(bundleRoot, input, secretsRoot, now);
189
+ const credentialPath = await storeHatchlingProviderCredentials(input.agentName, input.provider, input.credentials);
190
+ writeHatchlingProviderState(bundleRoot, input, now);
193
191
  writeDiaryScaffold(bundleRoot);
194
192
  writeFriendImprint(bundleRoot, input.humanName, now);
195
193
  writeHeartbeatHabit(bundleRoot, now);
@@ -202,7 +200,6 @@ async function runHatchFlow(input, deps = {}) {
202
200
  return {
203
201
  bundleRoot,
204
202
  selectedIdentity: selected.fileName,
205
- specialistSecretsPath,
206
- hatchlingSecretsPath,
203
+ credentialPath,
207
204
  };
208
205
  }
@@ -44,7 +44,7 @@ function buildSpecialistSystemPrompt(soulText, identityText, existingBundles, co
44
44
  `Provider: ${context.provider}`,
45
45
  `Temp directory: ${context.tempDir}`,
46
46
  "Final home: ~/AgentBundles/<Name>.ouro/",
47
- "Secrets: ~/.agentsecrets/<name>/secrets.json",
47
+ "Provider credentials: the hatch tool stores them in the agent's vault.",
48
48
  ].join("\n"));
49
49
  sections.push([
50
50
  "## Bundle creation guidelines",
@@ -42,7 +42,10 @@ const tools_base_1 = require("../../repertoire/tools-base");
42
42
  const hatch_flow_1 = require("./hatch-flow");
43
43
  const hatch_animation_1 = require("./hatch-animation");
44
44
  const bundle_manifest_1 = require("../../mind/bundle-manifest");
45
+ const identity_1 = require("../identity");
45
46
  const runtime_1 = require("../../nerves/runtime");
47
+ const vault_setup_1 = require("../../repertoire/vault-setup");
48
+ const vault_unlock_1 = require("../../repertoire/vault-unlock");
46
49
  const completeAdoptionTool = {
47
50
  type: "function",
48
51
  function: {
@@ -171,8 +174,17 @@ async function execCompleteAdoption(args, deps) {
171
174
  // Move tempDir -> final bundle location
172
175
  moveDir(deps.tempDir, targetBundle);
173
176
  // Write secrets
177
+ let generatedVaultUnlockSecret = null;
174
178
  try {
175
- (0, hatch_flow_1.writeSecretsFile)(name, deps.provider, deps.credentials, deps.secretsRoot);
179
+ const vault = (0, identity_1.resolveVaultConfig)(name);
180
+ const vaultUnlockSecret = crypto.randomBytes(32).toString("base64");
181
+ const vaultResult = await (0, vault_setup_1.createVaultAccount)(name, vault.serverUrl, vault.email, vaultUnlockSecret);
182
+ if (!vaultResult.success) {
183
+ throw new Error(`failed to create vault: ${vaultResult.error}`);
184
+ }
185
+ (0, vault_unlock_1.storeVaultUnlockSecret)({ agentName: name, email: vault.email, serverUrl: vault.serverUrl }, vaultUnlockSecret);
186
+ generatedVaultUnlockSecret = vaultUnlockSecret;
187
+ await (0, hatch_flow_1.storeHatchlingProviderCredentials)(name, deps.provider, deps.credentials);
176
188
  }
177
189
  catch (e) {
178
190
  // Rollback: remove the moved bundle
@@ -217,6 +229,14 @@ async function execCompleteAdoption(args, deps) {
217
229
  if (handoffMessage && deps.animationWriter) {
218
230
  deps.animationWriter(`\n${handoffMessage}\n`);
219
231
  }
232
+ if (generatedVaultUnlockSecret && deps.animationWriter) {
233
+ deps.animationWriter([
234
+ "",
235
+ `Vault unlock secret for ${name}: ${generatedVaultUnlockSecret}`,
236
+ `Use this with \`ouro vault unlock --agent ${name}\` on another machine.`,
237
+ "",
238
+ ].join("\n"));
239
+ }
220
240
  (0, runtime_1.emitNervesEvent)({
221
241
  component: "daemon",
222
242
  event: "daemon.adoption_complete",
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  // v1 → v2 agent.json migration.
3
- // Moves model from secrets.json into agent.json under humanFacing/agentFacing.
4
- // Uses raw fs — NO import from config.ts (avoids circular dependency).
3
+ // Creates explicit humanFacing/agentFacing blocks from the legacy provider.
4
+ // Uses raw fs and provider-model defaults — NO import from config.ts.
5
5
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
6
  if (k2 === undefined) k2 = k;
7
7
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -40,19 +40,15 @@ exports.migrateAgentConfigV1ToV2 = migrateAgentConfigV1ToV2;
40
40
  const fs = __importStar(require("fs"));
41
41
  const path = __importStar(require("path"));
42
42
  const runtime_1 = require("../nerves/runtime");
43
- const MODEL_FIELD = {
44
- azure: "modelName",
45
- minimax: "model",
46
- anthropic: "model",
47
- "openai-codex": "model",
48
- "github-copilot": "model",
49
- };
50
- function resolveSecretsPath(agentRoot, deps) {
51
- const agentName = path.basename(agentRoot).replace(/\.ouro$/, "");
52
- const secretsBase = deps?.secretsRoot ?? path.join(require("os").homedir(), ".agentsecrets");
53
- return path.join(secretsBase, agentName, "secrets.json");
43
+ const provider_models_1 = require("./provider-models");
44
+ function isAgentProvider(value) {
45
+ return value === "azure" ||
46
+ value === "minimax" ||
47
+ value === "anthropic" ||
48
+ value === "openai-codex" ||
49
+ value === "github-copilot";
54
50
  }
55
- function migrateAgentConfigV1ToV2(agentRoot, deps) {
51
+ function migrateAgentConfigV1ToV2(agentRoot) {
56
52
  const configPath = path.join(agentRoot, "agent.json");
57
53
  let raw;
58
54
  try {
@@ -84,44 +80,21 @@ function migrateAgentConfigV1ToV2(agentRoot, deps) {
84
80
  message: "migrating agent config v1 → v2",
85
81
  meta: { agentRoot },
86
82
  });
87
- const provider = config.provider;
88
- let model = "";
89
- if (provider) {
90
- const secretsPath = resolveSecretsPath(agentRoot, deps);
91
- try {
92
- const secretsRaw = fs.readFileSync(secretsPath, "utf-8");
93
- const secrets = JSON.parse(secretsRaw);
94
- const providers = secrets.providers;
95
- if (providers) {
96
- const providerSecrets = providers[provider];
97
- if (providerSecrets) {
98
- /* v8 ignore next -- fallback: all known providers are in MODEL_FIELD @preserve */
99
- const fieldName = MODEL_FIELD[provider] ?? "model";
100
- const rawModel = providerSecrets[fieldName];
101
- model = typeof rawModel === "string" ? rawModel : "";
102
- // Strip model from secrets
103
- delete providerSecrets[fieldName];
104
- fs.writeFileSync(secretsPath, JSON.stringify(secrets, null, 2) + "\n", "utf-8");
105
- }
106
- }
107
- }
108
- catch {
109
- // Missing or malformed secrets.json — leave model as empty string
110
- }
111
- }
83
+ const provider = isAgentProvider(config.provider) ? config.provider : "anthropic";
84
+ const model = (0, provider_models_1.getDefaultModelForProvider)(provider);
112
85
  // Write v2 config
113
86
  const { provider: _removed, ...rest } = config;
114
87
  const v2Config = {
115
88
  ...rest,
116
89
  version: 2,
117
- humanFacing: { provider: provider ?? "anthropic", model },
118
- agentFacing: { provider: provider ?? "anthropic", model },
90
+ humanFacing: { provider, model },
91
+ agentFacing: { provider, model },
119
92
  };
120
93
  fs.writeFileSync(configPath, JSON.stringify(v2Config, null, 2) + "\n", "utf-8");
121
94
  (0, runtime_1.emitNervesEvent)({
122
95
  component: "config/identity",
123
96
  event: "config_identity.migrate_end",
124
97
  message: "agent config migration complete",
125
- meta: { agentRoot, provider: provider ?? "unknown", model },
98
+ meta: { agentRoot, provider, model },
126
99
  });
127
100
  }
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.normalizeProviderLane = normalizeProviderLane;
4
4
  exports.resolveEffectiveProviderBinding = resolveEffectiveProviderBinding;
5
5
  const runtime_1 = require("../nerves/runtime");
6
- const provider_credential_pool_1 = require("./provider-credential-pool");
6
+ const provider_credentials_1 = require("./provider-credentials");
7
7
  const provider_state_1 = require("./provider-state");
8
8
  function legacyLaneWarning(selector, lane) {
9
9
  return {
@@ -36,7 +36,7 @@ function buildUseRepair(agentName, lane, force = false) {
36
36
  function buildAuthRepair(agentName, provider) {
37
37
  return {
38
38
  command: `ouro auth --agent ${agentName} --provider ${provider}`,
39
- message: `Authenticate ${provider} once for this machine's shared credential pool.`,
39
+ message: `Store ${provider} credentials in ${agentName}'s vault.`,
40
40
  };
41
41
  }
42
42
  function missingProviderStateWarning(agentName) {
@@ -54,13 +54,13 @@ function invalidProviderStateWarning(agentName) {
54
54
  function missingCredentialWarning(provider) {
55
55
  return {
56
56
  code: "credential-missing",
57
- message: `${provider} has no credential record in the machine credential pool.`,
57
+ message: `${provider} has no credential record in the agent vault.`,
58
58
  };
59
59
  }
60
60
  function invalidCredentialPoolWarning(provider) {
61
61
  return {
62
62
  code: "credential-pool-invalid",
63
- message: `${provider} cannot read credentials because the machine credential pool is invalid.`,
63
+ message: `${provider} cannot read credentials from the agent vault.`,
64
64
  };
65
65
  }
66
66
  function presentCredential(record) {
@@ -69,7 +69,6 @@ function presentCredential(record) {
69
69
  provider: record.provider,
70
70
  revision: record.revision,
71
71
  source: record.provenance.source,
72
- contributedByAgent: record.provenance.contributedByAgent,
73
72
  updatedAt: record.updatedAt,
74
73
  credentialFields: Object.keys(record.credentials).sort(),
75
74
  configFields: Object.keys(record.config).sort(),
@@ -90,7 +89,7 @@ function resolveCredential(poolResult, provider, agentName) {
90
89
  warnings: [missingCredentialWarning(provider)],
91
90
  };
92
91
  }
93
- if (poolResult.reason === "invalid") {
92
+ if (poolResult.reason === "invalid" || poolResult.reason === "unavailable") {
94
93
  return {
95
94
  credential: {
96
95
  status: "invalid-pool",
@@ -198,7 +197,7 @@ function resolveEffectiveProviderBinding(input) {
198
197
  return result;
199
198
  }
200
199
  const laneBinding = stateResult.state.lanes[laneResolution.lane];
201
- const poolResult = (0, provider_credential_pool_1.readProviderCredentialPool)(input.homeDir);
200
+ const poolResult = (0, provider_credentials_1.readProviderCredentialPool)(input.agentName);
202
201
  const credentialResult = resolveCredential(poolResult, laneBinding.provider, input.agentName);
203
202
  const readinessResult = resolveReadiness({
204
203
  provider: laneBinding.provider,