@ouro.bot/cli 0.1.0-alpha.8 → 0.1.0-alpha.81

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 (127) hide show
  1. package/AdoptionSpecialist.ouro/agent.json +70 -9
  2. package/AdoptionSpecialist.ouro/psyche/SOUL.md +5 -2
  3. package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
  4. package/README.md +147 -205
  5. package/assets/ouroboros.png +0 -0
  6. package/changelog.json +468 -0
  7. package/dist/heart/active-work.js +218 -0
  8. package/dist/heart/bridges/manager.js +358 -0
  9. package/dist/heart/bridges/state-machine.js +135 -0
  10. package/dist/heart/bridges/store.js +123 -0
  11. package/dist/heart/commitments.js +89 -0
  12. package/dist/heart/config.js +68 -23
  13. package/dist/heart/core.js +452 -93
  14. package/dist/heart/cross-chat-delivery.js +146 -0
  15. package/dist/heart/daemon/agent-discovery.js +81 -0
  16. package/dist/heart/daemon/auth-flow.js +430 -0
  17. package/dist/heart/daemon/daemon-cli.js +1779 -247
  18. package/dist/heart/daemon/daemon-entry.js +55 -6
  19. package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
  20. package/dist/heart/daemon/daemon.js +216 -10
  21. package/dist/heart/daemon/hatch-animation.js +10 -3
  22. package/dist/heart/daemon/hatch-flow.js +7 -82
  23. package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
  24. package/dist/heart/daemon/launchd.js +159 -0
  25. package/dist/heart/daemon/log-tailer.js +4 -3
  26. package/dist/heart/daemon/message-router.js +17 -8
  27. package/dist/heart/daemon/ouro-bot-entry.js +0 -0
  28. package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
  29. package/dist/heart/daemon/ouro-entry.js +0 -0
  30. package/dist/heart/daemon/ouro-path-installer.js +260 -0
  31. package/dist/heart/daemon/ouro-uti.js +11 -2
  32. package/dist/heart/daemon/ouro-version-manager.js +164 -0
  33. package/dist/heart/daemon/process-manager.js +14 -1
  34. package/dist/heart/daemon/run-hooks.js +37 -0
  35. package/dist/heart/daemon/runtime-logging.js +58 -15
  36. package/dist/heart/daemon/runtime-metadata.js +219 -0
  37. package/dist/heart/daemon/runtime-mode.js +67 -0
  38. package/dist/heart/daemon/sense-manager.js +307 -0
  39. package/dist/heart/daemon/skill-management-installer.js +94 -0
  40. package/dist/heart/daemon/socket-client.js +202 -0
  41. package/dist/heart/daemon/specialist-orchestrator.js +53 -84
  42. package/dist/heart/daemon/specialist-prompt.js +63 -11
  43. package/dist/heart/daemon/specialist-tools.js +211 -60
  44. package/dist/heart/daemon/staged-restart.js +114 -0
  45. package/dist/heart/daemon/thoughts.js +507 -0
  46. package/dist/heart/daemon/update-checker.js +111 -0
  47. package/dist/heart/daemon/update-hooks.js +138 -0
  48. package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
  49. package/dist/heart/delegation.js +62 -0
  50. package/dist/heart/identity.js +126 -21
  51. package/dist/heart/kicks.js +1 -19
  52. package/dist/heart/model-capabilities.js +48 -0
  53. package/dist/heart/obligations.js +141 -0
  54. package/dist/heart/progress-story.js +42 -0
  55. package/dist/heart/providers/anthropic.js +74 -9
  56. package/dist/heart/providers/azure.js +86 -7
  57. package/dist/heart/providers/github-copilot.js +149 -0
  58. package/dist/heart/providers/minimax.js +4 -0
  59. package/dist/heart/providers/openai-codex.js +12 -3
  60. package/dist/heart/safe-workspace.js +228 -0
  61. package/dist/heart/sense-truth.js +61 -0
  62. package/dist/heart/session-activity.js +169 -0
  63. package/dist/heart/session-recall.js +116 -0
  64. package/dist/heart/streaming.js +100 -22
  65. package/dist/heart/target-resolution.js +123 -0
  66. package/dist/heart/turn-coordinator.js +28 -0
  67. package/dist/mind/associative-recall.js +14 -2
  68. package/dist/mind/bundle-manifest.js +70 -0
  69. package/dist/mind/context.js +27 -11
  70. package/dist/mind/first-impressions.js +16 -2
  71. package/dist/mind/friends/channel.js +35 -0
  72. package/dist/mind/friends/group-context.js +144 -0
  73. package/dist/mind/friends/store-file.js +19 -0
  74. package/dist/mind/friends/trust-explanation.js +74 -0
  75. package/dist/mind/friends/types.js +8 -0
  76. package/dist/mind/memory.js +27 -26
  77. package/dist/mind/pending.js +76 -9
  78. package/dist/mind/phrases.js +1 -0
  79. package/dist/mind/prompt.js +445 -77
  80. package/dist/mind/token-estimate.js +8 -12
  81. package/dist/nerves/cli-logging.js +15 -2
  82. package/dist/nerves/coverage/run-artifacts.js +1 -1
  83. package/dist/nerves/index.js +12 -0
  84. package/dist/repertoire/ado-client.js +4 -2
  85. package/dist/repertoire/coding/feedback.js +134 -0
  86. package/dist/repertoire/coding/index.js +4 -1
  87. package/dist/repertoire/coding/manager.js +62 -4
  88. package/dist/repertoire/coding/spawner.js +3 -3
  89. package/dist/repertoire/coding/tools.js +41 -2
  90. package/dist/repertoire/data/ado-endpoints.json +188 -0
  91. package/dist/repertoire/guardrails.js +290 -0
  92. package/dist/repertoire/mcp-client.js +254 -0
  93. package/dist/repertoire/mcp-manager.js +195 -0
  94. package/dist/repertoire/skills.js +3 -26
  95. package/dist/repertoire/tasks/board.js +12 -0
  96. package/dist/repertoire/tasks/index.js +23 -9
  97. package/dist/repertoire/tasks/transitions.js +1 -2
  98. package/dist/repertoire/tools-base.js +686 -251
  99. package/dist/repertoire/tools-bluebubbles.js +93 -0
  100. package/dist/repertoire/tools-teams.js +58 -25
  101. package/dist/repertoire/tools.js +95 -53
  102. package/dist/senses/bluebubbles-client.js +210 -5
  103. package/dist/senses/bluebubbles-entry.js +2 -0
  104. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  105. package/dist/senses/bluebubbles-media.js +339 -0
  106. package/dist/senses/bluebubbles-model.js +12 -4
  107. package/dist/senses/bluebubbles-mutation-log.js +45 -5
  108. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  109. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  110. package/dist/senses/bluebubbles.js +894 -45
  111. package/dist/senses/cli-layout.js +187 -0
  112. package/dist/senses/cli.js +405 -156
  113. package/dist/senses/continuity.js +94 -0
  114. package/dist/senses/debug-activity.js +154 -0
  115. package/dist/senses/inner-dialog-worker.js +47 -18
  116. package/dist/senses/inner-dialog.js +377 -83
  117. package/dist/senses/pipeline.js +307 -0
  118. package/dist/senses/teams.js +573 -129
  119. package/dist/senses/trust-gate.js +112 -2
  120. package/package.json +14 -3
  121. package/subagents/README.md +4 -70
  122. package/dist/heart/daemon/specialist-session.js +0 -142
  123. package/dist/heart/daemon/subagent-installer.js +0 -125
  124. package/dist/inner-worker-entry.js +0 -4
  125. package/subagents/work-doer.md +0 -233
  126. package/subagents/work-merger.md +0 -624
  127. package/subagents/work-planner.md +0 -373
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deliverCrossChatMessage = deliverCrossChatMessage;
4
+ const types_1 = require("../mind/friends/types");
5
+ const runtime_1 = require("../nerves/runtime");
6
+ function buildPendingEnvelope(request, agentName, now) {
7
+ return {
8
+ from: agentName,
9
+ friendId: request.friendId,
10
+ channel: request.channel,
11
+ key: request.key,
12
+ content: request.content,
13
+ timestamp: now,
14
+ };
15
+ }
16
+ function queueForLater(request, deps, detail) {
17
+ deps.queuePending(buildPendingEnvelope(request, deps.agentName, (deps.now ?? Date.now)()));
18
+ return {
19
+ status: "queued_for_later",
20
+ detail,
21
+ };
22
+ }
23
+ function isExplicitlyAuthorized(request) {
24
+ return request.intent === "explicit_cross_chat"
25
+ && Boolean(request.authorizingSession)
26
+ && (0, types_1.isTrustedLevel)(request.authorizingSession?.trustLevel);
27
+ }
28
+ async function deliverCrossChatMessage(request, deps) {
29
+ (0, runtime_1.emitNervesEvent)({
30
+ component: "engine",
31
+ event: "engine.cross_chat_delivery_start",
32
+ message: "resolving cross-chat delivery",
33
+ meta: {
34
+ friendId: request.friendId,
35
+ channel: request.channel,
36
+ key: request.key,
37
+ intent: request.intent,
38
+ authorizingTrustLevel: request.authorizingSession?.trustLevel ?? null,
39
+ },
40
+ });
41
+ if (request.intent === "generic_outreach") {
42
+ const result = queueForLater(request, deps, "generic outreach stays queued until the target session is next active");
43
+ (0, runtime_1.emitNervesEvent)({
44
+ component: "engine",
45
+ event: "engine.cross_chat_delivery_end",
46
+ message: "queued generic outreach for later delivery",
47
+ meta: {
48
+ friendId: request.friendId,
49
+ channel: request.channel,
50
+ key: request.key,
51
+ status: result.status,
52
+ },
53
+ });
54
+ return result;
55
+ }
56
+ if (!isExplicitlyAuthorized(request)) {
57
+ const result = {
58
+ status: "blocked",
59
+ detail: "explicit cross-chat delivery requires a trusted asking session",
60
+ };
61
+ (0, runtime_1.emitNervesEvent)({
62
+ level: "warn",
63
+ component: "engine",
64
+ event: "engine.cross_chat_delivery_end",
65
+ message: "blocked explicit cross-chat delivery",
66
+ meta: {
67
+ friendId: request.friendId,
68
+ channel: request.channel,
69
+ key: request.key,
70
+ status: result.status,
71
+ },
72
+ });
73
+ return result;
74
+ }
75
+ const deliverer = deps.deliverers?.[request.channel];
76
+ if (!deliverer) {
77
+ const result = queueForLater(request, deps, "live delivery unavailable right now; queued for the next active turn");
78
+ (0, runtime_1.emitNervesEvent)({
79
+ component: "engine",
80
+ event: "engine.cross_chat_delivery_end",
81
+ message: "queued explicit cross-chat delivery because no live deliverer was available",
82
+ meta: {
83
+ friendId: request.friendId,
84
+ channel: request.channel,
85
+ key: request.key,
86
+ status: result.status,
87
+ },
88
+ });
89
+ return result;
90
+ }
91
+ try {
92
+ const direct = await deliverer(request);
93
+ if (direct.status === "delivered_now" || direct.status === "blocked" || direct.status === "failed") {
94
+ const result = {
95
+ status: direct.status,
96
+ detail: direct.detail,
97
+ };
98
+ (0, runtime_1.emitNervesEvent)({
99
+ level: result.status === "failed" ? "error" : result.status === "blocked" ? "warn" : "info",
100
+ component: "engine",
101
+ event: "engine.cross_chat_delivery_end",
102
+ message: "completed direct cross-chat delivery resolution",
103
+ meta: {
104
+ friendId: request.friendId,
105
+ channel: request.channel,
106
+ key: request.key,
107
+ status: result.status,
108
+ },
109
+ });
110
+ return result;
111
+ }
112
+ const result = queueForLater(request, deps, direct.detail.trim() || "live delivery unavailable right now; queued for the next active turn");
113
+ (0, runtime_1.emitNervesEvent)({
114
+ component: "engine",
115
+ event: "engine.cross_chat_delivery_end",
116
+ message: "queued explicit cross-chat delivery after adapter reported unavailability",
117
+ meta: {
118
+ friendId: request.friendId,
119
+ channel: request.channel,
120
+ key: request.key,
121
+ status: result.status,
122
+ },
123
+ });
124
+ return result;
125
+ }
126
+ catch (error) {
127
+ const result = {
128
+ status: "failed",
129
+ detail: error instanceof Error ? error.message : String(error),
130
+ };
131
+ (0, runtime_1.emitNervesEvent)({
132
+ level: "error",
133
+ component: "engine",
134
+ event: "engine.cross_chat_delivery_end",
135
+ message: "cross-chat delivery threw unexpectedly",
136
+ meta: {
137
+ friendId: request.friendId,
138
+ channel: request.channel,
139
+ key: request.key,
140
+ status: result.status,
141
+ reason: result.detail,
142
+ },
143
+ });
144
+ return result;
145
+ }
146
+ }
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.listEnabledBundleAgents = listEnabledBundleAgents;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const identity_1 = require("../identity");
40
+ const runtime_1 = require("../../nerves/runtime");
41
+ function listEnabledBundleAgents(options = {}) {
42
+ const bundlesRoot = options.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
43
+ const readdirSync = options.readdirSync ?? fs.readdirSync;
44
+ const readFileSync = options.readFileSync ?? fs.readFileSync;
45
+ let entries;
46
+ try {
47
+ entries = readdirSync(bundlesRoot, { withFileTypes: true });
48
+ }
49
+ catch {
50
+ (0, runtime_1.emitNervesEvent)({
51
+ level: "warn",
52
+ component: "daemon",
53
+ event: "daemon.agent_discovery_failed",
54
+ message: "failed to read bundle root for daemon agent discovery",
55
+ meta: { bundlesRoot },
56
+ });
57
+ return [];
58
+ }
59
+ const discovered = [];
60
+ for (const entry of entries) {
61
+ if (!entry.isDirectory() || !entry.name.endsWith(".ouro"))
62
+ continue;
63
+ const agentName = entry.name.slice(0, -5);
64
+ const configPath = path.join(bundlesRoot, entry.name, "agent.json");
65
+ let enabled = true;
66
+ try {
67
+ const raw = readFileSync(configPath, "utf-8");
68
+ const parsed = JSON.parse(raw);
69
+ if (typeof parsed.enabled === "boolean") {
70
+ enabled = parsed.enabled;
71
+ }
72
+ }
73
+ catch {
74
+ continue;
75
+ }
76
+ if (enabled) {
77
+ discovered.push(agentName);
78
+ }
79
+ }
80
+ return discovered.sort((left, right) => left.localeCompare(right));
81
+ }
@@ -0,0 +1,430 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.readAgentConfigForAgent = readAgentConfigForAgent;
37
+ exports.writeAgentProviderSelection = writeAgentProviderSelection;
38
+ exports.loadAgentSecrets = loadAgentSecrets;
39
+ exports.writeProviderCredentials = writeProviderCredentials;
40
+ exports.writeAgentModel = writeAgentModel;
41
+ exports.collectRuntimeAuthCredentials = collectRuntimeAuthCredentials;
42
+ exports.resolveHatchCredentials = resolveHatchCredentials;
43
+ exports.runRuntimeAuthFlow = runRuntimeAuthFlow;
44
+ const child_process_1 = require("child_process");
45
+ const fs = __importStar(require("fs"));
46
+ const os = __importStar(require("os"));
47
+ const path = __importStar(require("path"));
48
+ const runtime_1 = require("../../nerves/runtime");
49
+ const identity_1 = require("../identity");
50
+ const ANTHROPIC_SETUP_TOKEN_PREFIX = "sk-ant-oat01-";
51
+ const ANTHROPIC_SETUP_TOKEN_MIN_LENGTH = 80;
52
+ const DEFAULT_SECRETS_TEMPLATE = {
53
+ providers: {
54
+ azure: {
55
+ modelName: "gpt-4o-mini",
56
+ apiKey: "",
57
+ endpoint: "",
58
+ deployment: "",
59
+ apiVersion: "2025-04-01-preview",
60
+ },
61
+ minimax: {
62
+ model: "minimax-text-01",
63
+ apiKey: "",
64
+ },
65
+ anthropic: {
66
+ model: "claude-opus-4-6",
67
+ setupToken: "",
68
+ },
69
+ "openai-codex": {
70
+ model: "gpt-5.4",
71
+ oauthAccessToken: "",
72
+ },
73
+ "github-copilot": {
74
+ model: "claude-sonnet-4.6",
75
+ githubToken: "",
76
+ baseUrl: "",
77
+ },
78
+ },
79
+ teams: {
80
+ clientId: "",
81
+ clientSecret: "",
82
+ tenantId: "",
83
+ },
84
+ oauth: {
85
+ graphConnectionName: "graph",
86
+ adoConnectionName: "ado",
87
+ githubConnectionName: "",
88
+ },
89
+ teamsChannel: {
90
+ skipConfirmation: true,
91
+ port: 3978,
92
+ },
93
+ integrations: {
94
+ perplexityApiKey: "",
95
+ openaiEmbeddingsApiKey: "",
96
+ },
97
+ };
98
+ function deepMerge(defaults, partial) {
99
+ const result = { ...defaults };
100
+ for (const key of Object.keys(partial)) {
101
+ const left = result[key];
102
+ const right = partial[key];
103
+ if (right !== null &&
104
+ typeof right === "object" &&
105
+ !Array.isArray(right) &&
106
+ left !== null &&
107
+ typeof left === "object" &&
108
+ !Array.isArray(left)) {
109
+ result[key] = deepMerge(left, right);
110
+ continue;
111
+ }
112
+ result[key] = right;
113
+ }
114
+ return result;
115
+ }
116
+ function readJsonRecord(filePath, label) {
117
+ try {
118
+ const raw = fs.readFileSync(filePath, "utf8");
119
+ const parsed = JSON.parse(raw);
120
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
121
+ throw new Error("expected object");
122
+ }
123
+ return parsed;
124
+ }
125
+ catch (error) {
126
+ throw new Error(`Failed to read ${label} at ${filePath}: ${String(error)}`);
127
+ }
128
+ }
129
+ function readAgentConfigForAgent(agentName, bundlesRoot = (0, identity_1.getAgentBundlesRoot)()) {
130
+ const configPath = path.join(bundlesRoot, `${agentName}.ouro`, "agent.json");
131
+ const parsed = readJsonRecord(configPath, "agent config");
132
+ const provider = parsed.provider;
133
+ if (provider !== "azure" &&
134
+ provider !== "anthropic" &&
135
+ provider !== "minimax" &&
136
+ provider !== "openai-codex" &&
137
+ provider !== "github-copilot") {
138
+ throw new Error(`agent.json at ${configPath} has unsupported provider '${String(provider)}'`);
139
+ }
140
+ return {
141
+ configPath,
142
+ config: parsed,
143
+ };
144
+ }
145
+ function writeAgentProviderSelection(agentName, provider, bundlesRoot = (0, identity_1.getAgentBundlesRoot)()) {
146
+ const { configPath, config } = readAgentConfigForAgent(agentName, bundlesRoot);
147
+ const nextConfig = { ...config, provider };
148
+ fs.writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
149
+ (0, runtime_1.emitNervesEvent)({
150
+ component: "daemon",
151
+ event: "daemon.auth_provider_selected",
152
+ message: "updated agent provider selection after auth flow",
153
+ meta: { agentName, provider, configPath },
154
+ });
155
+ return configPath;
156
+ }
157
+ function resolveAgentSecretsPath(agentName, deps = {}) {
158
+ if (deps.secretsRoot)
159
+ return path.join(deps.secretsRoot, agentName, "secrets.json");
160
+ const homeDir = deps.homeDir ?? os.homedir();
161
+ return (0, identity_1.getAgentSecretsPath)(agentName).replace(os.homedir(), homeDir);
162
+ }
163
+ function loadAgentSecrets(agentName, deps = {}) {
164
+ const secretsPath = resolveAgentSecretsPath(agentName, deps);
165
+ const secretsDir = path.dirname(secretsPath);
166
+ fs.mkdirSync(secretsDir, { recursive: true });
167
+ let onDisk = {};
168
+ try {
169
+ onDisk = readJsonRecord(secretsPath, "secrets config");
170
+ }
171
+ catch (error) {
172
+ const message = error.message;
173
+ if (!message.includes("ENOENT"))
174
+ throw error;
175
+ }
176
+ return {
177
+ secretsPath,
178
+ secrets: deepMerge(DEFAULT_SECRETS_TEMPLATE, onDisk),
179
+ };
180
+ }
181
+ function writeSecrets(secretsPath, secrets) {
182
+ fs.writeFileSync(secretsPath, `${JSON.stringify(secrets, null, 2)}\n`, "utf8");
183
+ }
184
+ function writeProviderCredentials(agentName, provider, credentials, deps = {}) {
185
+ const { secretsPath, secrets } = loadAgentSecrets(agentName, deps);
186
+ applyCredentials(secrets, provider, credentials);
187
+ writeSecrets(secretsPath, secrets);
188
+ return { secretsPath, secrets };
189
+ }
190
+ const MODEL_FIELD = {
191
+ azure: "modelName",
192
+ minimax: "model",
193
+ anthropic: "model",
194
+ "openai-codex": "model",
195
+ "github-copilot": "model",
196
+ };
197
+ function writeAgentModel(agentName, modelName, deps = {}) {
198
+ const { config } = readAgentConfigForAgent(agentName, deps.bundlesRoot);
199
+ const provider = config.provider;
200
+ const { secretsPath, secrets } = loadAgentSecrets(agentName, deps);
201
+ const providerSecrets = secrets.providers[provider];
202
+ /* v8 ignore next -- fallback: all known providers are in MODEL_FIELD @preserve */
203
+ const fieldName = MODEL_FIELD[provider] ?? "model";
204
+ /* v8 ignore next -- defensive: fieldName always exists in template @preserve */
205
+ const previousModel = providerSecrets[fieldName] ?? "";
206
+ providerSecrets[fieldName] = modelName;
207
+ writeSecrets(secretsPath, secrets);
208
+ return { secretsPath, provider, previousModel };
209
+ }
210
+ function readCodexAccessToken(homeDir) {
211
+ const authPath = path.join(homeDir, ".codex", "auth.json");
212
+ try {
213
+ const raw = fs.readFileSync(authPath, "utf8");
214
+ const parsed = JSON.parse(raw);
215
+ const token = parsed?.tokens?.access_token;
216
+ return typeof token === "string" ? token.trim() : "";
217
+ }
218
+ catch {
219
+ return "";
220
+ }
221
+ }
222
+ function ensurePromptInput(promptInput, provider) {
223
+ if (promptInput)
224
+ return promptInput;
225
+ throw new Error(`No prompt input is available for ${provider} authentication.`);
226
+ }
227
+ function validateAnthropicToken(token) {
228
+ const trimmed = token.trim();
229
+ if (!trimmed) {
230
+ throw new Error("No Anthropic setup token was provided.");
231
+ }
232
+ if (!trimmed.startsWith(ANTHROPIC_SETUP_TOKEN_PREFIX)) {
233
+ throw new Error(`Invalid Anthropic setup token format. Expected prefix ${ANTHROPIC_SETUP_TOKEN_PREFIX}.`);
234
+ }
235
+ if (trimmed.length < ANTHROPIC_SETUP_TOKEN_MIN_LENGTH) {
236
+ throw new Error("Anthropic setup token looks too short.");
237
+ }
238
+ return trimmed;
239
+ }
240
+ async function collectRuntimeAuthCredentials(input, deps) {
241
+ const spawnSync = deps.spawnSync ?? child_process_1.spawnSync;
242
+ const homeDir = deps.homeDir ?? os.homedir();
243
+ if (input.provider === "github-copilot") {
244
+ let token = process.env.GH_TOKEN?.trim() || "";
245
+ if (!token) {
246
+ const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
247
+ token = (result.status === 0 && result.stdout ? result.stdout.trim() : "");
248
+ }
249
+ if (!token) {
250
+ (0, runtime_1.emitNervesEvent)({
251
+ component: "daemon",
252
+ event: "daemon.auth_gh_login_start",
253
+ message: "starting gh auth login for runtime auth",
254
+ meta: { agentName: input.agentName },
255
+ });
256
+ const loginResult = spawnSync("gh", ["auth", "login"], { stdio: "inherit" });
257
+ if (loginResult.status !== 0) {
258
+ throw new Error("'gh auth login' failed. Install the GitHub CLI (gh) and try again.");
259
+ }
260
+ const retryResult = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
261
+ /* v8 ignore next -- branch: retry after login always succeeds in tests @preserve */
262
+ token = (retryResult.status === 0 && retryResult.stdout ? retryResult.stdout.trim() : "");
263
+ /* v8 ignore next -- defensive: gh auth login succeeded but token still missing @preserve */
264
+ if (!token) {
265
+ throw new Error("gh auth login completed but no token was found. Run `gh auth login` and try again.");
266
+ }
267
+ }
268
+ const response = await fetch("https://api.github.com/copilot_internal/user", {
269
+ headers: { Authorization: `Bearer ${token}` },
270
+ });
271
+ if (!response.ok) {
272
+ throw new Error(`GitHub Copilot endpoint discovery failed (HTTP ${response.status}). Ensure your GitHub account has Copilot access.`);
273
+ }
274
+ const body = await response.json();
275
+ const baseUrl = body?.endpoints?.api;
276
+ /* v8 ignore next -- defensive: valid response but missing endpoints field @preserve */
277
+ if (!baseUrl) {
278
+ throw new Error("GitHub Copilot endpoint discovery returned no endpoints.api. Ensure your GitHub account has Copilot access.");
279
+ }
280
+ return { githubToken: token, baseUrl };
281
+ }
282
+ if (input.provider === "openai-codex") {
283
+ let token = readCodexAccessToken(homeDir);
284
+ if (!token) {
285
+ (0, runtime_1.emitNervesEvent)({
286
+ component: "daemon",
287
+ event: "daemon.auth_codex_login_start",
288
+ message: "starting codex login for runtime auth",
289
+ meta: { agentName: input.agentName },
290
+ });
291
+ const result = spawnSync("codex", ["login"], { stdio: "inherit" });
292
+ if (result.error) {
293
+ throw new Error(`Failed to run 'codex login': ${result.error.message}`);
294
+ }
295
+ if (result.status !== 0) {
296
+ throw new Error(`'codex login' exited with status ${result.status}.`);
297
+ }
298
+ token = readCodexAccessToken(homeDir);
299
+ if (!token) {
300
+ throw new Error("Codex login completed but no token was found in ~/.codex/auth.json. Re-run `codex login` and try again.");
301
+ }
302
+ }
303
+ return { oauthAccessToken: token };
304
+ }
305
+ if (input.provider === "anthropic") {
306
+ (0, runtime_1.emitNervesEvent)({
307
+ component: "daemon",
308
+ event: "daemon.auth_claude_setup_start",
309
+ message: "starting claude setup-token for runtime auth",
310
+ meta: { agentName: input.agentName },
311
+ });
312
+ const result = spawnSync("claude", ["setup-token"], { stdio: "inherit" });
313
+ if (result.error) {
314
+ throw new Error(`Failed to run 'claude setup-token': ${result.error.message}`);
315
+ }
316
+ if (result.status !== 0) {
317
+ throw new Error(`'claude setup-token' exited with status ${result.status}.`);
318
+ }
319
+ const prompt = ensurePromptInput(input.promptInput, input.provider);
320
+ const setupToken = validateAnthropicToken(await prompt("Paste the setup token from `claude setup-token`: "));
321
+ return { setupToken };
322
+ }
323
+ if (input.provider === "minimax") {
324
+ const prompt = ensurePromptInput(input.promptInput, input.provider);
325
+ const apiKey = (await prompt("MiniMax API key: ")).trim();
326
+ if (!apiKey)
327
+ throw new Error("MiniMax API key is required.");
328
+ return { apiKey };
329
+ }
330
+ const prompt = ensurePromptInput(input.promptInput, input.provider);
331
+ const apiKey = (await prompt("Azure API key: ")).trim();
332
+ const endpoint = (await prompt("Azure endpoint: ")).trim();
333
+ const deployment = (await prompt("Azure deployment: ")).trim();
334
+ if (!apiKey || !endpoint || !deployment) {
335
+ throw new Error("Azure API key, endpoint, and deployment are required.");
336
+ }
337
+ return { apiKey, endpoint, deployment };
338
+ }
339
+ async function resolveHatchCredentials(input) {
340
+ const prompt = input.promptInput;
341
+ const credentials = { ...(input.credentials ?? {}) };
342
+ if (input.provider === "github-copilot" && !credentials.githubToken && input.runAuthFlow) {
343
+ const result = await input.runAuthFlow({
344
+ agentName: input.agentName,
345
+ provider: "github-copilot",
346
+ promptInput: prompt,
347
+ });
348
+ Object.assign(credentials, result.credentials);
349
+ }
350
+ if (input.provider === "anthropic" && !credentials.setupToken && input.runAuthFlow) {
351
+ const result = await input.runAuthFlow({
352
+ agentName: input.agentName,
353
+ provider: "anthropic",
354
+ promptInput: prompt,
355
+ });
356
+ Object.assign(credentials, result.credentials);
357
+ }
358
+ if (input.provider === "anthropic" && !credentials.setupToken && prompt) {
359
+ credentials.setupToken = await prompt("Anthropic setup-token: ");
360
+ }
361
+ if (input.provider === "openai-codex" && !credentials.oauthAccessToken && input.runAuthFlow) {
362
+ const result = await input.runAuthFlow({
363
+ agentName: input.agentName,
364
+ provider: "openai-codex",
365
+ promptInput: prompt,
366
+ });
367
+ Object.assign(credentials, result.credentials);
368
+ }
369
+ if (input.provider === "openai-codex" && !credentials.oauthAccessToken && prompt) {
370
+ credentials.oauthAccessToken = await prompt("OpenAI Codex OAuth token: ");
371
+ }
372
+ if (input.provider === "minimax" && !credentials.apiKey && prompt) {
373
+ credentials.apiKey = await prompt("MiniMax API key: ");
374
+ }
375
+ if (input.provider === "azure") {
376
+ if (!credentials.apiKey && prompt)
377
+ credentials.apiKey = await prompt("Azure API key: ");
378
+ if (!credentials.endpoint && prompt)
379
+ credentials.endpoint = await prompt("Azure endpoint: ");
380
+ if (!credentials.deployment && prompt)
381
+ credentials.deployment = await prompt("Azure deployment: ");
382
+ }
383
+ return credentials;
384
+ }
385
+ function applyCredentials(secrets, provider, credentials) {
386
+ if (provider === "anthropic") {
387
+ secrets.providers.anthropic.setupToken = credentials.setupToken.trim();
388
+ return;
389
+ }
390
+ if (provider === "github-copilot") {
391
+ secrets.providers["github-copilot"].githubToken = credentials.githubToken.trim();
392
+ secrets.providers["github-copilot"].baseUrl = credentials.baseUrl.trim();
393
+ return;
394
+ }
395
+ if (provider === "openai-codex") {
396
+ secrets.providers["openai-codex"].oauthAccessToken = credentials.oauthAccessToken.trim();
397
+ return;
398
+ }
399
+ if (provider === "minimax") {
400
+ secrets.providers.minimax.apiKey = credentials.apiKey.trim();
401
+ return;
402
+ }
403
+ secrets.providers.azure.apiKey = credentials.apiKey.trim();
404
+ secrets.providers.azure.endpoint = credentials.endpoint.trim();
405
+ secrets.providers.azure.deployment = credentials.deployment.trim();
406
+ }
407
+ async function runRuntimeAuthFlow(input, deps = {}) {
408
+ (0, runtime_1.emitNervesEvent)({
409
+ component: "daemon",
410
+ event: "daemon.auth_flow_start",
411
+ message: "starting runtime auth flow",
412
+ meta: { agentName: input.agentName, provider: input.provider },
413
+ });
414
+ const homeDir = deps.homeDir ?? os.homedir();
415
+ const credentials = await collectRuntimeAuthCredentials(input, deps);
416
+ const { secretsPath } = writeProviderCredentials(input.agentName, input.provider, credentials, { homeDir });
417
+ (0, runtime_1.emitNervesEvent)({
418
+ component: "daemon",
419
+ event: "daemon.auth_flow_end",
420
+ message: "completed runtime auth flow",
421
+ meta: { agentName: input.agentName, provider: input.provider, secretsPath },
422
+ });
423
+ return {
424
+ agentName: input.agentName,
425
+ provider: input.provider,
426
+ secretsPath,
427
+ message: `authenticated ${input.agentName} with ${input.provider}`,
428
+ credentials,
429
+ };
430
+ }