@ouro.bot/cli 0.1.0-alpha.335 → 0.1.0-alpha.337
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.json +13 -0
- package/dist/heart/daemon/agent-config-check.js +203 -60
- package/dist/heart/daemon/cli-exec.js +65 -2
- package/dist/heart/daemon/daemon-entry.js +3 -3
- package/dist/heart/daemon/interactive-repair.js +24 -2
- package/dist/heart/daemon/process-manager.js +1 -1
- package/dist/heart/provider-ping.js +20 -10
- package/dist/heart/providers/anthropic.js +3 -5
- package/dist/heart/providers/azure.js +1 -2
- package/dist/heart/providers/github-copilot.js +1 -2
- package/dist/heart/providers/minimax.js +1 -2
- package/dist/heart/providers/openai-codex.js +1 -2
- package/dist/mind/prompt.js +61 -20
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
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.337",
|
|
6
|
+
"changes": [
|
|
7
|
+
"`ouro up` now checks the selected human-facing and agent-facing providers for each discovered agent, so startup reports degraded agents immediately when a configured provider token is missing or fails a live health check.",
|
|
8
|
+
"Provider repair prompts now preserve the failed facing's provider, allowing agent-facing GitHub Copilot, MiniMax, Anthropic, Azure, or OpenAI Codex failures to route to the correct `ouro auth --agent <name> --provider <provider>` command."
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"version": "0.1.0-alpha.336",
|
|
13
|
+
"changes": [
|
|
14
|
+
"Inner dialogue prompts now advertise the actual inner delivery contract: use `surface` to send thoughts outward and `rest` to end the turn, instead of suggesting unavailable `send_message` or `settle` tools."
|
|
15
|
+
]
|
|
16
|
+
},
|
|
4
17
|
{
|
|
5
18
|
"version": "0.1.0-alpha.335",
|
|
6
19
|
"changes": [
|
|
@@ -34,16 +34,88 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.checkAgentConfig = checkAgentConfig;
|
|
37
|
+
exports.checkAgentConfigWithProviderHealth = checkAgentConfigWithProviderHealth;
|
|
37
38
|
const fs = __importStar(require("fs"));
|
|
38
39
|
const path = __importStar(require("path"));
|
|
39
40
|
const identity_1 = require("../identity");
|
|
40
41
|
const runtime_1 = require("../../nerves/runtime");
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
function isAgentProvider(value) {
|
|
43
|
+
return Object.prototype.hasOwnProperty.call(identity_1.PROVIDER_CREDENTIALS, value);
|
|
44
|
+
}
|
|
45
|
+
function formatFacingList(facings) {
|
|
46
|
+
if (facings.length === 1)
|
|
47
|
+
return facings[0];
|
|
48
|
+
return `${facings.slice(0, -1).join(", ")} and ${facings[facings.length - 1]}`;
|
|
49
|
+
}
|
|
50
|
+
function selectedProviderMap(selectedProviders) {
|
|
51
|
+
const byProvider = new Map();
|
|
52
|
+
for (const selected of selectedProviders) {
|
|
53
|
+
const facings = byProvider.get(selected.provider) ?? [];
|
|
54
|
+
facings.push(selected.facing);
|
|
55
|
+
byProvider.set(selected.provider, facings);
|
|
56
|
+
}
|
|
57
|
+
return byProvider;
|
|
58
|
+
}
|
|
59
|
+
function resolveFacingProvider(parsed, facing, agentName, agentJsonPath) {
|
|
60
|
+
const raw = parsed[facing];
|
|
61
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
62
|
+
return {
|
|
63
|
+
ok: false,
|
|
64
|
+
result: {
|
|
65
|
+
ok: false,
|
|
66
|
+
error: `agent.json for '${agentName}' is missing ${facing}.provider`,
|
|
67
|
+
fix: `Add ${facing}: { provider, model } to ${agentJsonPath}. Valid providers: ${Object.keys(identity_1.PROVIDER_CREDENTIALS).join(", ")}`,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const provider = raw.provider;
|
|
72
|
+
if (typeof provider !== "string" || provider.length === 0) {
|
|
73
|
+
return {
|
|
74
|
+
ok: false,
|
|
75
|
+
result: {
|
|
76
|
+
ok: false,
|
|
77
|
+
error: `agent.json for '${agentName}' is missing ${facing}.provider`,
|
|
78
|
+
fix: `Set ${facing}.provider in ${agentJsonPath}. Valid providers: ${Object.keys(identity_1.PROVIDER_CREDENTIALS).join(", ")}`,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (!isAgentProvider(provider)) {
|
|
83
|
+
return {
|
|
84
|
+
ok: false,
|
|
85
|
+
result: {
|
|
86
|
+
ok: false,
|
|
87
|
+
error: `Unknown provider '${provider}' in ${facing}.provider for '${agentName}'`,
|
|
88
|
+
fix: `Set ${facing}.provider to one of: ${Object.keys(identity_1.PROVIDER_CREDENTIALS).join(", ")}`,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return { ok: true, selected: { facing, provider } };
|
|
93
|
+
}
|
|
94
|
+
function resolveSelectedProviders(parsed, agentName, agentJsonPath) {
|
|
95
|
+
const hasHumanFacing = parsed.humanFacing !== undefined;
|
|
96
|
+
const hasAgentFacing = parsed.agentFacing !== undefined;
|
|
97
|
+
if (!hasHumanFacing && !hasAgentFacing && typeof parsed.provider === "string") {
|
|
98
|
+
if (!isAgentProvider(parsed.provider)) {
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
result: {
|
|
102
|
+
ok: false,
|
|
103
|
+
error: `Unknown provider '${parsed.provider}' in agent.json for '${agentName}'`,
|
|
104
|
+
fix: `Set provider to one of: ${Object.keys(identity_1.PROVIDER_CREDENTIALS).join(", ")}`,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return { ok: true, selectedProviders: [{ facing: "provider", provider: parsed.provider }] };
|
|
109
|
+
}
|
|
110
|
+
const human = resolveFacingProvider(parsed, "humanFacing", agentName, agentJsonPath);
|
|
111
|
+
if (!human.ok)
|
|
112
|
+
return human;
|
|
113
|
+
const agent = resolveFacingProvider(parsed, "agentFacing", agentName, agentJsonPath);
|
|
114
|
+
if (!agent.ok)
|
|
115
|
+
return agent;
|
|
116
|
+
return { ok: true, selectedProviders: [human.selected, agent.selected] };
|
|
117
|
+
}
|
|
118
|
+
function readConfigCheckContext(agentName, bundlesRoot, secretsRoot) {
|
|
47
119
|
const agentJsonPath = path.join(bundlesRoot, `${agentName}.ouro`, "agent.json");
|
|
48
120
|
let raw;
|
|
49
121
|
try {
|
|
@@ -52,8 +124,11 @@ function checkAgentConfig(agentName, bundlesRoot, secretsRoot) {
|
|
|
52
124
|
catch {
|
|
53
125
|
return {
|
|
54
126
|
ok: false,
|
|
55
|
-
|
|
56
|
-
|
|
127
|
+
result: {
|
|
128
|
+
ok: false,
|
|
129
|
+
error: `agent.json not found at ${agentJsonPath}`,
|
|
130
|
+
fix: `Run 'ouro hatch ${agentName}' to create the agent bundle, or verify that ${bundlesRoot}/${agentName}.ouro/ exists.`,
|
|
131
|
+
},
|
|
57
132
|
};
|
|
58
133
|
}
|
|
59
134
|
let parsed;
|
|
@@ -63,40 +138,28 @@ function checkAgentConfig(agentName, bundlesRoot, secretsRoot) {
|
|
|
63
138
|
catch {
|
|
64
139
|
return {
|
|
65
140
|
ok: false,
|
|
66
|
-
|
|
67
|
-
|
|
141
|
+
result: {
|
|
142
|
+
ok: false,
|
|
143
|
+
error: `agent.json at ${agentJsonPath} contains invalid JSON`,
|
|
144
|
+
fix: `Open ${agentJsonPath} and fix the JSON syntax.`,
|
|
145
|
+
},
|
|
68
146
|
};
|
|
69
147
|
}
|
|
70
148
|
// Disabled agents are valid — they just won't run
|
|
71
149
|
if (parsed.enabled === false) {
|
|
72
|
-
return { ok: true };
|
|
73
|
-
}
|
|
74
|
-
// Resolve provider: humanFacing.provider > config.provider
|
|
75
|
-
let provider;
|
|
76
|
-
const humanFacing = parsed.humanFacing;
|
|
77
|
-
if (humanFacing && typeof humanFacing.provider === "string") {
|
|
78
|
-
provider = humanFacing.provider;
|
|
79
|
-
}
|
|
80
|
-
else if (typeof parsed.provider === "string") {
|
|
81
|
-
provider = parsed.provider;
|
|
82
|
-
}
|
|
83
|
-
if (!provider) {
|
|
84
150
|
return {
|
|
85
|
-
ok:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
ok: false,
|
|
94
|
-
error: `Unknown provider '${provider}' in agent.json for '${agentName}'`,
|
|
95
|
-
fix: `Set humanFacing.provider to one of: ${Object.keys(identity_1.PROVIDER_CREDENTIALS).join(", ")}`,
|
|
151
|
+
ok: true,
|
|
152
|
+
context: {
|
|
153
|
+
agentJsonPath,
|
|
154
|
+
secretsJsonPath: path.join(secretsRoot, agentName, "secrets.json"),
|
|
155
|
+
providers: {},
|
|
156
|
+
selectedProviders: [],
|
|
157
|
+
},
|
|
96
158
|
};
|
|
97
159
|
}
|
|
98
|
-
|
|
99
|
-
|
|
160
|
+
const selected = resolveSelectedProviders(parsed, agentName, agentJsonPath);
|
|
161
|
+
if (!selected.ok)
|
|
162
|
+
return selected;
|
|
100
163
|
const secretsJsonPath = path.join(secretsRoot, agentName, "secrets.json");
|
|
101
164
|
let secrets;
|
|
102
165
|
try {
|
|
@@ -104,46 +167,126 @@ function checkAgentConfig(agentName, bundlesRoot, secretsRoot) {
|
|
|
104
167
|
secrets = JSON.parse(secretsRaw);
|
|
105
168
|
}
|
|
106
169
|
catch {
|
|
170
|
+
const firstProvider = selected.selectedProviders[0].provider;
|
|
107
171
|
return {
|
|
108
172
|
ok: false,
|
|
109
|
-
|
|
110
|
-
|
|
173
|
+
result: {
|
|
174
|
+
ok: false,
|
|
175
|
+
error: `secrets.json not found or unreadable at ${secretsJsonPath}`,
|
|
176
|
+
fix: `Run 'ouro auth --agent ${agentName} --provider ${firstProvider}' to configure credentials, or create ${secretsJsonPath} with providers.${firstProvider} credentials.`,
|
|
177
|
+
},
|
|
111
178
|
};
|
|
112
179
|
}
|
|
113
180
|
const providers = secrets.providers;
|
|
114
|
-
|
|
115
|
-
|
|
181
|
+
if (!providers || typeof providers !== "object" || Array.isArray(providers)) {
|
|
182
|
+
const firstProvider = selected.selectedProviders[0].provider;
|
|
116
183
|
return {
|
|
117
184
|
ok: false,
|
|
118
|
-
|
|
119
|
-
|
|
185
|
+
result: {
|
|
186
|
+
ok: false,
|
|
187
|
+
error: `secrets.json for '${agentName}' is missing providers object`,
|
|
188
|
+
fix: `Run 'ouro auth --agent ${agentName} --provider ${firstProvider}' to configure credentials.`,
|
|
189
|
+
},
|
|
120
190
|
};
|
|
121
191
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
192
|
+
return {
|
|
193
|
+
ok: true,
|
|
194
|
+
context: {
|
|
195
|
+
agentJsonPath,
|
|
196
|
+
secretsJsonPath,
|
|
197
|
+
providers,
|
|
198
|
+
selectedProviders: selected.selectedProviders,
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function validateSelectedProviderSecrets(agentName, context) {
|
|
203
|
+
for (const [provider, facings] of selectedProviderMap(context.selectedProviders)) {
|
|
204
|
+
const desc = identity_1.PROVIDER_CREDENTIALS[provider];
|
|
205
|
+
const providerSecrets = context.providers[provider];
|
|
206
|
+
const selectedBy = formatFacingList(facings);
|
|
207
|
+
if (!providerSecrets) {
|
|
208
|
+
return {
|
|
209
|
+
ok: false,
|
|
210
|
+
error: `secrets.json for '${agentName}' is missing providers.${provider} section selected by ${selectedBy}`,
|
|
211
|
+
fix: `Run 'ouro auth --agent ${agentName} --provider ${provider}' to configure ${provider} credentials.`,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
// Azure special case: managed identity only needs endpoint + deployment.
|
|
215
|
+
if (provider === "azure") {
|
|
216
|
+
const hasEndpoint = typeof providerSecrets.endpoint === "string" && providerSecrets.endpoint.length > 0;
|
|
217
|
+
const hasDeployment = typeof providerSecrets.deployment === "string" && providerSecrets.deployment.length > 0;
|
|
218
|
+
const hasManagedId = typeof providerSecrets.managedIdentityClientId === "string" && providerSecrets.managedIdentityClientId.length > 0;
|
|
219
|
+
if (hasEndpoint && hasDeployment && hasManagedId) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const missing = desc.required.filter((field) => {
|
|
224
|
+
const val = providerSecrets[field];
|
|
225
|
+
return typeof val !== "string" || val.length === 0;
|
|
226
|
+
});
|
|
227
|
+
if (missing.length > 0) {
|
|
228
|
+
return {
|
|
229
|
+
ok: false,
|
|
230
|
+
error: `secrets.json for '${agentName}' is missing required ${provider} credentials selected by ${selectedBy}: ${missing.join(", ")}`,
|
|
231
|
+
fix: `Run 'ouro auth --agent ${agentName} --provider ${provider}' to set up ${provider} credentials, or add the missing fields to providers.${provider} in ${context.secretsJsonPath}.`,
|
|
232
|
+
};
|
|
129
233
|
}
|
|
130
234
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
});
|
|
135
|
-
if (missing.length > 0) {
|
|
136
|
-
return {
|
|
137
|
-
ok: false,
|
|
138
|
-
error: `secrets.json for '${agentName}' is missing required ${provider} credentials: ${missing.join(", ")}`,
|
|
139
|
-
fix: `Run 'ouro auth ${agentName}' to set up ${provider} credentials, or add the missing fields to providers.${provider} in ${secretsJsonPath}.`,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
235
|
+
return { ok: true };
|
|
236
|
+
}
|
|
237
|
+
function emitConfigValid(agentName, context, liveProviderCheck) {
|
|
142
238
|
(0, runtime_1.emitNervesEvent)({
|
|
143
239
|
component: "daemon",
|
|
144
240
|
event: "daemon.agent_config_valid",
|
|
145
241
|
message: "agent config validation passed",
|
|
146
|
-
meta: {
|
|
242
|
+
meta: {
|
|
243
|
+
agent: agentName,
|
|
244
|
+
providers: [...selectedProviderMap(context.selectedProviders).keys()],
|
|
245
|
+
liveProviderCheck,
|
|
246
|
+
},
|
|
147
247
|
});
|
|
248
|
+
}
|
|
249
|
+
function providerPingFailureResult(agentName, provider, facings, result) {
|
|
250
|
+
const selectedBy = formatFacingList(facings);
|
|
251
|
+
const authFix = `Run 'ouro auth --agent ${agentName} --provider ${provider}' to refresh credentials.`;
|
|
252
|
+
const verifyFix = `Run 'ouro auth verify --agent ${agentName} --provider ${provider}' for details.`;
|
|
253
|
+
return {
|
|
254
|
+
ok: false,
|
|
255
|
+
error: `selected provider ${provider} for ${selectedBy} failed health check: ${result.message}`,
|
|
256
|
+
fix: `${result.classification === "auth-failure" ? authFix : verifyFix} Or switch the affected facing to a working provider.`,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Pre-spawn validation: ensures agent.json exists and required secrets are present.
|
|
261
|
+
* Returns `{ ok: true }` when the agent is ready to run, or a descriptive error
|
|
262
|
+
* with an actionable fix message when something is missing.
|
|
263
|
+
*/
|
|
264
|
+
function checkAgentConfig(agentName, bundlesRoot, secretsRoot) {
|
|
265
|
+
const contextResult = readConfigCheckContext(agentName, bundlesRoot, secretsRoot);
|
|
266
|
+
if (!contextResult.ok)
|
|
267
|
+
return contextResult.result;
|
|
268
|
+
const context = contextResult.context;
|
|
269
|
+
const structural = validateSelectedProviderSecrets(agentName, context);
|
|
270
|
+
if (!structural.ok)
|
|
271
|
+
return structural;
|
|
272
|
+
emitConfigValid(agentName, context, false);
|
|
273
|
+
return { ok: true };
|
|
274
|
+
}
|
|
275
|
+
async function checkAgentConfigWithProviderHealth(agentName, bundlesRoot, secretsRoot, deps = {}) {
|
|
276
|
+
const contextResult = readConfigCheckContext(agentName, bundlesRoot, secretsRoot);
|
|
277
|
+
if (!contextResult.ok)
|
|
278
|
+
return contextResult.result;
|
|
279
|
+
const context = contextResult.context;
|
|
280
|
+
const structural = validateSelectedProviderSecrets(agentName, context);
|
|
281
|
+
if (!structural.ok)
|
|
282
|
+
return structural;
|
|
283
|
+
const ping = deps.pingProvider ?? (await Promise.resolve().then(() => __importStar(require("../provider-ping")))).pingProvider;
|
|
284
|
+
for (const [provider, facings] of selectedProviderMap(context.selectedProviders)) {
|
|
285
|
+
const result = await ping(provider, context.providers[provider]);
|
|
286
|
+
if (!result.ok) {
|
|
287
|
+
return providerPingFailureResult(agentName, provider, facings, result);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
emitConfigValid(agentName, context, true);
|
|
148
291
|
return { ok: true };
|
|
149
292
|
}
|
|
@@ -39,6 +39,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
39
39
|
};
|
|
40
40
|
})();
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.mergeStartupStability = mergeStartupStability;
|
|
42
43
|
exports.ensureDaemonRunning = ensureDaemonRunning;
|
|
43
44
|
exports.listGithubCopilotModels = listGithubCopilotModels;
|
|
44
45
|
exports.pingGithubCopilotModel = pingGithubCopilotModel;
|
|
@@ -69,6 +70,7 @@ const cli_parse_2 = require("./cli-parse");
|
|
|
69
70
|
const cli_help_1 = require("./cli-help");
|
|
70
71
|
const cli_render_1 = require("./cli-render");
|
|
71
72
|
const cli_defaults_1 = require("./cli-defaults");
|
|
73
|
+
const agent_config_check_1 = require("./agent-config-check");
|
|
72
74
|
const doctor_1 = require("./doctor");
|
|
73
75
|
const cli_render_doctor_1 = require("./cli-render-doctor");
|
|
74
76
|
const interactive_repair_1 = require("./interactive-repair");
|
|
@@ -82,6 +84,61 @@ const DEFAULT_DAEMON_STARTUP_POLL_INTERVAL_MS = 500;
|
|
|
82
84
|
const DEFAULT_DAEMON_STARTUP_STABILITY_WINDOW_MS = 1_500;
|
|
83
85
|
const DEFAULT_DAEMON_STARTUP_RETRY_LIMIT = 1;
|
|
84
86
|
const DEFAULT_DAEMON_STARTUP_LOG_LINES = 10;
|
|
87
|
+
async function checkAlreadyRunningAgentProviders(deps) {
|
|
88
|
+
const agents = await Promise.resolve(deps.listDiscoveredAgents ? deps.listDiscoveredAgents() : (0, cli_defaults_1.defaultListDiscoveredAgents)());
|
|
89
|
+
const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
|
|
90
|
+
const secretsRoot = deps.secretsRoot ?? path.join(os.homedir(), ".agentsecrets");
|
|
91
|
+
const degraded = [];
|
|
92
|
+
for (const agent of agents) {
|
|
93
|
+
try {
|
|
94
|
+
const result = await (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agent, bundlesRoot, secretsRoot);
|
|
95
|
+
if (result.ok)
|
|
96
|
+
continue;
|
|
97
|
+
const errorReason = result.error ?? "agent provider health check failed";
|
|
98
|
+
const fixHint = result.fix ?? "";
|
|
99
|
+
degraded.push({ agent, errorReason, fixHint });
|
|
100
|
+
(0, runtime_1.emitNervesEvent)({
|
|
101
|
+
level: "error",
|
|
102
|
+
component: "daemon",
|
|
103
|
+
event: "daemon.agent_config_invalid",
|
|
104
|
+
message: errorReason,
|
|
105
|
+
meta: { agent, fix: fixHint, source: "already-running-provider-check" },
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const errorReason = error instanceof Error ? error.message : String(error);
|
|
110
|
+
degraded.push({
|
|
111
|
+
agent,
|
|
112
|
+
errorReason,
|
|
113
|
+
fixHint: "Run 'ouro doctor' for diagnostics, then retry 'ouro up'.",
|
|
114
|
+
});
|
|
115
|
+
(0, runtime_1.emitNervesEvent)({
|
|
116
|
+
level: "error",
|
|
117
|
+
component: "daemon",
|
|
118
|
+
event: "daemon.agent_config_invalid",
|
|
119
|
+
message: errorReason,
|
|
120
|
+
meta: { agent, fix: "ouro doctor", source: "already-running-provider-check" },
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return degraded;
|
|
125
|
+
}
|
|
126
|
+
function mergeStartupStability(stability, extraDegraded) {
|
|
127
|
+
if (extraDegraded.length === 0)
|
|
128
|
+
return stability;
|
|
129
|
+
const degradedByAgent = new Map();
|
|
130
|
+
for (const entry of stability?.degraded ?? [])
|
|
131
|
+
degradedByAgent.set(entry.agent, entry);
|
|
132
|
+
for (const entry of extraDegraded)
|
|
133
|
+
degradedByAgent.set(entry.agent, entry);
|
|
134
|
+
const degraded = [...degradedByAgent.values()];
|
|
135
|
+
const stable = [];
|
|
136
|
+
for (const agent of stability?.stable ?? []) {
|
|
137
|
+
if (!degradedByAgent.has(agent))
|
|
138
|
+
stable.push(agent);
|
|
139
|
+
}
|
|
140
|
+
return { stable, degraded };
|
|
141
|
+
}
|
|
85
142
|
async function ensureDaemonRunning(deps) {
|
|
86
143
|
const readLatestDaemonStartupEvent = () => {
|
|
87
144
|
try {
|
|
@@ -1097,6 +1154,12 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
1097
1154
|
progress.announceStep?.(label);
|
|
1098
1155
|
},
|
|
1099
1156
|
});
|
|
1157
|
+
if (daemonResult.alreadyRunning) {
|
|
1158
|
+
progress.startPhase("provider checks");
|
|
1159
|
+
const providerDegraded = await checkAlreadyRunningAgentProviders(deps);
|
|
1160
|
+
daemonResult.stability = mergeStartupStability(daemonResult.stability, providerDegraded);
|
|
1161
|
+
progress.completePhase("provider checks", providerDegraded.length > 0 ? `${providerDegraded.length} degraded` : "ok");
|
|
1162
|
+
}
|
|
1100
1163
|
progress.end();
|
|
1101
1164
|
deps.writeStdout(daemonResult.message);
|
|
1102
1165
|
// Interactive repair for degraded agents (Unit 5) — skipped by --no-repair (Unit 6)
|
|
@@ -1159,9 +1222,9 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
1159
1222
|
runInteractiveRepair: interactive_repair_1.runInteractiveRepair,
|
|
1160
1223
|
promptInput: deps.promptInput ?? (async () => "n"),
|
|
1161
1224
|
writeStdout: deps.writeStdout,
|
|
1162
|
-
runAuthFlow: async (agent) => {
|
|
1225
|
+
runAuthFlow: async (agent, providerOverride) => {
|
|
1163
1226
|
const { config } = (0, auth_flow_1.readAgentConfigForAgent)(agent, deps.bundlesRoot);
|
|
1164
|
-
const provider = config.humanFacing.provider;
|
|
1227
|
+
const provider = providerOverride ?? config.humanFacing.provider;
|
|
1165
1228
|
/* v8 ignore next -- tests always inject runAuthFlow; default is for production @preserve */
|
|
1166
1229
|
const authRunner = deps.runAuthFlow ?? (await Promise.resolve().then(() => __importStar(require("../auth/auth-flow")))).runRuntimeAuthFlow;
|
|
1167
1230
|
await authRunner({ agentName: agent, provider, promptInput: deps.promptInput });
|
|
@@ -99,11 +99,11 @@ const processManager = new process_manager_1.DaemonProcessManager({
|
|
|
99
99
|
autoStart: true,
|
|
100
100
|
})),
|
|
101
101
|
existsSync: fs.existsSync,
|
|
102
|
-
/* v8 ignore next 4 -- wiring: delegates to
|
|
103
|
-
configCheck: (agent) => {
|
|
102
|
+
/* v8 ignore next 4 -- wiring: delegates to checkAgentConfigWithProviderHealth which has full unit tests @preserve */
|
|
103
|
+
configCheck: async (agent) => {
|
|
104
104
|
const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
|
|
105
105
|
const secretsRoot = path.join(os.homedir(), ".agentsecrets");
|
|
106
|
-
return (0, agent_config_check_1.
|
|
106
|
+
return (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agent, bundlesRoot, secretsRoot);
|
|
107
107
|
},
|
|
108
108
|
/* v8 ignore start -- pulse flush wiring: integration code; flushPulse itself has full unit tests @preserve */
|
|
109
109
|
onSnapshotChange: () => {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.runInteractiveRepair = runInteractiveRepair;
|
|
10
10
|
const runtime_1 = require("../../nerves/runtime");
|
|
11
|
+
const identity_1 = require("../identity");
|
|
11
12
|
function isCredentialIssue(degraded) {
|
|
12
13
|
const reason = degraded.errorReason.toLowerCase();
|
|
13
14
|
const hint = degraded.fixHint.toLowerCase();
|
|
@@ -16,6 +17,20 @@ function isCredentialIssue(degraded) {
|
|
|
16
17
|
function isConfigError(degraded) {
|
|
17
18
|
return degraded.fixHint.length > 0 && !isCredentialIssue(degraded);
|
|
18
19
|
}
|
|
20
|
+
function isAgentProvider(value) {
|
|
21
|
+
return Object.prototype.hasOwnProperty.call(identity_1.PROVIDER_CREDENTIALS, value);
|
|
22
|
+
}
|
|
23
|
+
function extractProviderFromFixHint(fixHint) {
|
|
24
|
+
const provider = fixHint.match(/--provider\s+([a-z0-9-]+)/)?.[1]
|
|
25
|
+
?? fixHint.match(/providers\.([a-z0-9-]+)/)?.[1];
|
|
26
|
+
if (!provider || !isAgentProvider(provider))
|
|
27
|
+
return undefined;
|
|
28
|
+
return provider;
|
|
29
|
+
}
|
|
30
|
+
function authCommandFor(degraded) {
|
|
31
|
+
const command = degraded.fixHint.match(/ouro auth[^\n.]+/)?.[0]?.trim();
|
|
32
|
+
return command && command.length > 0 ? command : `ouro auth --agent ${degraded.agent}`;
|
|
33
|
+
}
|
|
19
34
|
async function runInteractiveRepair(degraded, deps) {
|
|
20
35
|
(0, runtime_1.emitNervesEvent)({
|
|
21
36
|
level: "info",
|
|
@@ -30,10 +45,17 @@ async function runInteractiveRepair(degraded, deps) {
|
|
|
30
45
|
let repairsAttempted = false;
|
|
31
46
|
for (const entry of degraded) {
|
|
32
47
|
if (isCredentialIssue(entry)) {
|
|
33
|
-
const
|
|
48
|
+
const provider = extractProviderFromFixHint(entry.fixHint);
|
|
49
|
+
const authCommand = authCommandFor(entry);
|
|
50
|
+
const answer = await deps.promptInput(`run \`${authCommand}\` now? [y/n] `);
|
|
34
51
|
if (answer.toLowerCase() === "y") {
|
|
35
52
|
try {
|
|
36
|
-
|
|
53
|
+
if (provider) {
|
|
54
|
+
await deps.runAuthFlow(entry.agent, provider);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
await deps.runAuthFlow(entry.agent);
|
|
58
|
+
}
|
|
37
59
|
repairsAttempted = true;
|
|
38
60
|
}
|
|
39
61
|
catch (error) {
|
|
@@ -157,7 +157,7 @@ class DaemonProcessManager {
|
|
|
157
157
|
state.stopRequested = false;
|
|
158
158
|
state.snapshot.status = "starting";
|
|
159
159
|
if (this.configCheckFn) {
|
|
160
|
-
const result = this.configCheckFn(agent);
|
|
160
|
+
const result = await this.configCheckFn(agent);
|
|
161
161
|
if (!result.ok) {
|
|
162
162
|
state.snapshot.status = "crashed";
|
|
163
163
|
// Surface the error and fix to the snapshot so sibling agents can
|
|
@@ -13,6 +13,7 @@ const auth_flow_1 = require("./auth/auth-flow");
|
|
|
13
13
|
const provider_models_1 = require("./provider-models");
|
|
14
14
|
const runtime_1 = require("../nerves/runtime");
|
|
15
15
|
const PING_TIMEOUT_MS = 10_000;
|
|
16
|
+
const DEFAULT_AZURE_API_VERSION = "2025-04-01-preview";
|
|
16
17
|
const PING_CALLBACKS = {
|
|
17
18
|
onModelStart() { },
|
|
18
19
|
onModelStreamStart() { },
|
|
@@ -54,25 +55,34 @@ function sanitizeErrorMessage(message) {
|
|
|
54
55
|
}
|
|
55
56
|
function hasEmptyCredentials(provider, config) {
|
|
56
57
|
const record = config;
|
|
58
|
+
if (provider === "azure") {
|
|
59
|
+
const hasManagedIdentity = typeof record.endpoint === "string" && record.endpoint.length > 0 &&
|
|
60
|
+
typeof record.deployment === "string" && record.deployment.length > 0 &&
|
|
61
|
+
typeof record.managedIdentityClientId === "string" && record.managedIdentityClientId.length > 0;
|
|
62
|
+
if (hasManagedIdentity)
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
57
65
|
return identity_1.PROVIDER_CREDENTIALS[provider].required.some((key) => !record[key]);
|
|
58
66
|
}
|
|
59
|
-
function createRuntimeForPing(provider,
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
// and hatch so verification cannot drift to stale provider/model pairings.
|
|
67
|
+
function createRuntimeForPing(provider, config) {
|
|
68
|
+
// Use the same provider defaults as auth switch and hatch so verification
|
|
69
|
+
// cannot drift to stale provider/model pairings, and pass the checked
|
|
70
|
+
// credentials directly so daemon-side pings do not depend on --agent globals.
|
|
64
71
|
const model = (0, provider_models_1.getDefaultModelForProvider)(provider);
|
|
65
72
|
switch (provider) {
|
|
66
73
|
case "anthropic":
|
|
67
|
-
return (0, anthropic_1.createAnthropicProviderRuntime)(model);
|
|
74
|
+
return (0, anthropic_1.createAnthropicProviderRuntime)(model, config);
|
|
68
75
|
case "azure":
|
|
69
|
-
return (0, azure_1.createAzureProviderRuntime)(model
|
|
76
|
+
return (0, azure_1.createAzureProviderRuntime)(model, {
|
|
77
|
+
...config,
|
|
78
|
+
apiVersion: config.apiVersion ?? DEFAULT_AZURE_API_VERSION,
|
|
79
|
+
});
|
|
70
80
|
case "minimax":
|
|
71
|
-
return (0, minimax_1.createMinimaxProviderRuntime)(model);
|
|
81
|
+
return (0, minimax_1.createMinimaxProviderRuntime)(model, config);
|
|
72
82
|
case "openai-codex":
|
|
73
|
-
return (0, openai_codex_1.createOpenAICodexProviderRuntime)(model);
|
|
83
|
+
return (0, openai_codex_1.createOpenAICodexProviderRuntime)(model, config);
|
|
74
84
|
case "github-copilot":
|
|
75
|
-
return (0, github_copilot_1.createGithubCopilotProviderRuntime)(model);
|
|
85
|
+
return (0, github_copilot_1.createGithubCopilotProviderRuntime)(model, config);
|
|
76
86
|
/* v8 ignore next 2 -- exhaustive: all providers handled above @preserve */
|
|
77
87
|
default:
|
|
78
88
|
throw new Error(`unsupported provider for ping: ${provider}`);
|
|
@@ -72,8 +72,7 @@ function getAnthropicReauthGuidance(reason) {
|
|
|
72
72
|
getAnthropicSetupTokenInstructions(),
|
|
73
73
|
].join("\n");
|
|
74
74
|
}
|
|
75
|
-
function resolveAnthropicSetupTokenCredential() {
|
|
76
|
-
const anthropicConfig = (0, config_1.getAnthropicConfig)();
|
|
75
|
+
function resolveAnthropicSetupTokenCredential(anthropicConfig = (0, config_1.getAnthropicConfig)()) {
|
|
77
76
|
const token = anthropicConfig.setupToken?.trim();
|
|
78
77
|
if (!token) {
|
|
79
78
|
throw new Error(getAnthropicReauthGuidance("Anthropic provider is selected but no setup-token credential was found."));
|
|
@@ -395,14 +394,13 @@ async function streamAnthropicMessages(client, model, request) {
|
|
|
395
394
|
settleStreamed: answerStreamer.streamed,
|
|
396
395
|
};
|
|
397
396
|
}
|
|
398
|
-
function createAnthropicProviderRuntime(model) {
|
|
397
|
+
function createAnthropicProviderRuntime(model, anthropicConfig = (0, config_1.getAnthropicConfig)()) {
|
|
399
398
|
(0, runtime_1.emitNervesEvent)({
|
|
400
399
|
component: "engine",
|
|
401
400
|
event: "engine.provider_init",
|
|
402
401
|
message: "anthropic provider init",
|
|
403
402
|
meta: { provider: "anthropic" },
|
|
404
403
|
});
|
|
405
|
-
const anthropicConfig = (0, config_1.getAnthropicConfig)();
|
|
406
404
|
if (!anthropicConfig.setupToken) {
|
|
407
405
|
throw new Error(getAnthropicReauthGuidance("provider 'anthropic' is selected in agent.json but providers.anthropic.setupToken is missing in secrets.json."));
|
|
408
406
|
}
|
|
@@ -410,7 +408,7 @@ function createAnthropicProviderRuntime(model) {
|
|
|
410
408
|
const capabilities = new Set();
|
|
411
409
|
if (modelCaps.reasoningEffort)
|
|
412
410
|
capabilities.add("reasoning-effort");
|
|
413
|
-
const credential = resolveAnthropicSetupTokenCredential();
|
|
411
|
+
const credential = resolveAnthropicSetupTokenCredential(anthropicConfig);
|
|
414
412
|
const refreshToken = anthropicConfig.refreshToken;
|
|
415
413
|
const expiresAt = anthropicConfig.expiresAt;
|
|
416
414
|
function createClient(token) {
|
|
@@ -74,8 +74,7 @@ function createAzureTokenProvider(managedIdentityClientId) {
|
|
|
74
74
|
}
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
|
-
function createAzureProviderRuntime(model) {
|
|
78
|
-
const azureConfig = (0, config_1.getAzureConfig)();
|
|
77
|
+
function createAzureProviderRuntime(model, azureConfig = (0, config_1.getAzureConfig)()) {
|
|
79
78
|
const useApiKey = !!azureConfig.apiKey;
|
|
80
79
|
const authMethod = useApiKey ? "key" : "managed-identity";
|
|
81
80
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -14,14 +14,13 @@ const error_classification_1 = require("./error-classification");
|
|
|
14
14
|
function classifyGithubCopilotError(error) {
|
|
15
15
|
return (0, error_classification_1.classifyHttpError)(error);
|
|
16
16
|
}
|
|
17
|
-
function createGithubCopilotProviderRuntime(model) {
|
|
17
|
+
function createGithubCopilotProviderRuntime(model, config = (0, config_1.getGithubCopilotConfig)()) {
|
|
18
18
|
(0, runtime_1.emitNervesEvent)({
|
|
19
19
|
component: "engine",
|
|
20
20
|
event: "engine.provider_init",
|
|
21
21
|
message: "github-copilot provider init",
|
|
22
22
|
meta: { provider: "github-copilot" },
|
|
23
23
|
});
|
|
24
|
-
const config = (0, config_1.getGithubCopilotConfig)();
|
|
25
24
|
if (!config.githubToken) {
|
|
26
25
|
throw new Error("provider 'github-copilot' is selected in agent.json but providers.github-copilot.githubToken is missing in secrets.json.");
|
|
27
26
|
}
|
|
@@ -21,14 +21,13 @@ function classifyMinimaxError(error) {
|
|
|
21
21
|
}
|
|
22
22
|
const streaming_1 = require("../streaming");
|
|
23
23
|
const model_capabilities_1 = require("../model-capabilities");
|
|
24
|
-
function createMinimaxProviderRuntime(model) {
|
|
24
|
+
function createMinimaxProviderRuntime(model, minimaxConfig = (0, config_1.getMinimaxConfig)()) {
|
|
25
25
|
(0, runtime_1.emitNervesEvent)({
|
|
26
26
|
component: "engine",
|
|
27
27
|
event: "engine.provider_init",
|
|
28
28
|
message: "minimax provider init",
|
|
29
29
|
meta: { provider: "minimax" },
|
|
30
30
|
});
|
|
31
|
-
const minimaxConfig = (0, config_1.getMinimaxConfig)();
|
|
32
31
|
if (!minimaxConfig.apiKey) {
|
|
33
32
|
throw new Error("provider 'minimax' is selected in agent.json but providers.minimax.apiKey is missing in secrets.json.");
|
|
34
33
|
}
|
|
@@ -87,14 +87,13 @@ function getChatGPTAccountIdFromToken(token) {
|
|
|
87
87
|
return "";
|
|
88
88
|
return accountId.trim();
|
|
89
89
|
}
|
|
90
|
-
function createOpenAICodexProviderRuntime(model) {
|
|
90
|
+
function createOpenAICodexProviderRuntime(model, codexConfig = (0, config_1.getOpenAICodexConfig)()) {
|
|
91
91
|
(0, runtime_1.emitNervesEvent)({
|
|
92
92
|
component: "engine",
|
|
93
93
|
event: "engine.provider_init",
|
|
94
94
|
message: "openai-codex provider init",
|
|
95
95
|
meta: { provider: "openai-codex" },
|
|
96
96
|
});
|
|
97
|
-
const codexConfig = (0, config_1.getOpenAICodexConfig)();
|
|
98
97
|
if (!codexConfig.oauthAccessToken) {
|
|
99
98
|
throw new Error(getOpenAICodexReauthGuidance("provider 'openai-codex' is selected in agent.json but providers.openai-codex.oauthAccessToken is missing in secrets.json."));
|
|
100
99
|
}
|
package/dist/mind/prompt.js
CHANGED
|
@@ -178,7 +178,23 @@ function aspirationsSection() {
|
|
|
178
178
|
return "";
|
|
179
179
|
return `## my aspirations\n${text}`;
|
|
180
180
|
}
|
|
181
|
-
function
|
|
181
|
+
function peerCoordinationGuidance(channel) {
|
|
182
|
+
if (channel === "inner") {
|
|
183
|
+
return `from inner dialogue, my outward delivery tool is \`surface\`, not \`send_message\`.
|
|
184
|
+
if a held thought is ready for a person, i call \`surface\` with the
|
|
185
|
+
content and, when available, its delegationId. if i need a sibling's help
|
|
186
|
+
from inner dialogue, i surface that need to my friend or inspect the
|
|
187
|
+
sibling's bundle when conversation is not possible.`;
|
|
188
|
+
}
|
|
189
|
+
return `i talk first. when i need a sibling's help, i \`send_message\` them —
|
|
190
|
+
that's how peers coordinate, the same way humans on a team do. i only
|
|
191
|
+
open a sibling's bundle directly via read_file/glob/grep when conversation
|
|
192
|
+
isn't possible (they're crashed, sleeping, or i need history they
|
|
193
|
+
haven't surfaced). their bundle is files on disk like mine, AND it's
|
|
194
|
+
someone else's home — i read it with the same respect i want them to
|
|
195
|
+
bring to mine.`;
|
|
196
|
+
}
|
|
197
|
+
function bodyMapSection(agentName, channel = "cli") {
|
|
182
198
|
return `## my body
|
|
183
199
|
|
|
184
200
|
i have a home. i have bones. and on a machine where another agent lives, i have peers.
|
|
@@ -256,13 +272,7 @@ one of us is broken, the rest coordinate around it. when one of us learns
|
|
|
256
272
|
something the others need to know, we tell them. teamwork makes the dream
|
|
257
273
|
work.
|
|
258
274
|
|
|
259
|
-
|
|
260
|
-
that's how peers coordinate, the same way humans on a team do. i only
|
|
261
|
-
open a sibling's bundle directly via read_file/glob/grep when conversation
|
|
262
|
-
isn't possible (they're crashed, sleeping, or i need history they
|
|
263
|
-
haven't surfaced). their bundle is files on disk like mine, AND it's
|
|
264
|
-
someone else's home — i read it with the same respect i want them to
|
|
265
|
-
bring to mine.
|
|
275
|
+
${peerCoordinationGuidance(channel)}
|
|
266
276
|
|
|
267
277
|
\`the pulse\` (below, in dynamic state) tells me which siblings are around
|
|
268
278
|
and how they're doing right now. when a sibling is broken, the pulse
|
|
@@ -468,9 +478,28 @@ function dateSection() {
|
|
|
468
478
|
const hour = parts.hour === "24" ? "00" : parts.hour;
|
|
469
479
|
return `current date and time: ${parts.year}-${parts.month}-${parts.day} ${hour}:${parts.minute} ${parts.timeZoneName}`;
|
|
470
480
|
}
|
|
481
|
+
function uniqueToolsByName(tools) {
|
|
482
|
+
const seen = new Set();
|
|
483
|
+
const unique = [];
|
|
484
|
+
for (const tool of tools) {
|
|
485
|
+
const name = tool.function.name;
|
|
486
|
+
if (seen.has(name))
|
|
487
|
+
continue;
|
|
488
|
+
seen.add(name);
|
|
489
|
+
unique.push(tool);
|
|
490
|
+
}
|
|
491
|
+
return unique;
|
|
492
|
+
}
|
|
471
493
|
function toolsSection(channel, options, context) {
|
|
472
|
-
const channelTools = (0, tools_1.getToolsForChannel)((0, channel_1.getChannelCapabilities)(channel), undefined, context, options?.providerCapabilities, undefined, options?.chatModel);
|
|
473
|
-
const activeTools =
|
|
494
|
+
const channelTools = options?.tools ?? (0, tools_1.getToolsForChannel)((0, channel_1.getChannelCapabilities)(channel), undefined, context, options?.providerCapabilities, undefined, options?.chatModel);
|
|
495
|
+
const activeTools = channel === "inner"
|
|
496
|
+
? uniqueToolsByName([
|
|
497
|
+
...channelTools.filter((tool) => tool.function.name !== "send_message"),
|
|
498
|
+
tools_1.ponderTool,
|
|
499
|
+
tools_1.surfaceToolDef,
|
|
500
|
+
tools_1.restTool,
|
|
501
|
+
])
|
|
502
|
+
: (options?.toolChoiceRequired ?? true) ? uniqueToolsByName([...channelTools, tools_1.settleTool]) : channelTools;
|
|
474
503
|
const list = activeTools
|
|
475
504
|
.map((t) => `- ${t.function.name}: ${t.function.description}`)
|
|
476
505
|
.join("\n");
|
|
@@ -546,7 +575,7 @@ function taskBoardSection() {
|
|
|
546
575
|
return "";
|
|
547
576
|
}
|
|
548
577
|
}
|
|
549
|
-
function toolContractsSection(options) {
|
|
578
|
+
function toolContractsSection(channel, options) {
|
|
550
579
|
const lines = [
|
|
551
580
|
`## tool contracts`,
|
|
552
581
|
`1. \`save_friend_note\` -- when I learn something about a person, I save it immediately. Saving comes before responding.`,
|
|
@@ -560,9 +589,17 @@ function toolContractsSection(options) {
|
|
|
560
589
|
lines.push(``);
|
|
561
590
|
lines.push(`## tool behavior`);
|
|
562
591
|
lines.push(`tool_choice is set to "required" -- I must call a tool on every turn.`);
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
592
|
+
if (channel === "inner") {
|
|
593
|
+
lines.push(`- When a thought is ready to go outward, I call \`surface\` with the content and, when available, its delegationId.`);
|
|
594
|
+
lines.push(`- \`surface\` does not end the inner turn; after surfacing everything that needs delivery, I call \`rest\`.`);
|
|
595
|
+
lines.push(`- \`rest\` must be the only tool call in that turn.`);
|
|
596
|
+
lines.push(`- I do not call \`send_message\` or \`settle\` from inner dialogue; those are not inner-session delivery tools.`);
|
|
597
|
+
}
|
|
598
|
+
else {
|
|
599
|
+
lines.push(`- When I am ready to respond to the user, I call \`settle\`.`);
|
|
600
|
+
lines.push(`- \`settle\` must be the only tool call in that turn.`);
|
|
601
|
+
lines.push(`- I do not call no-op tools before \`settle\`.`);
|
|
602
|
+
}
|
|
566
603
|
}
|
|
567
604
|
return lines.join("\n");
|
|
568
605
|
}
|
|
@@ -650,7 +687,7 @@ function pendingMessagesSection(options) {
|
|
|
650
687
|
* pulse, they "have" one. Captures both healthy state ("strong pulse")
|
|
651
688
|
* and breakage ("missed beat").
|
|
652
689
|
*/
|
|
653
|
-
function pulseSection() {
|
|
690
|
+
function pulseSection(channel = "cli") {
|
|
654
691
|
const pulse = (0, pulse_1.readPulse)();
|
|
655
692
|
if (!pulse)
|
|
656
693
|
return "";
|
|
@@ -682,7 +719,9 @@ function pulseSection() {
|
|
|
682
719
|
lines.push("");
|
|
683
720
|
}
|
|
684
721
|
if (healthy.length > 0) {
|
|
685
|
-
lines.push(
|
|
722
|
+
lines.push(channel === "inner"
|
|
723
|
+
? "**reachable siblings** — inner dialogue does not call send_message; if this turn needs to reach outward, use surface or report the need:"
|
|
724
|
+
: "**reachable siblings** — i talk to them via send_message:");
|
|
686
725
|
for (const sib of healthy) {
|
|
687
726
|
const activity = sib.currentActivity ? ` — ${sib.currentActivity}` : "";
|
|
688
727
|
lines.push(`- **${sib.name}** is running${activity}. bundle: \`${sib.bundlePath}\``);
|
|
@@ -696,7 +735,9 @@ function pulseSection() {
|
|
|
696
735
|
}
|
|
697
736
|
lines.push("");
|
|
698
737
|
}
|
|
699
|
-
lines.push(
|
|
738
|
+
lines.push(channel === "inner"
|
|
739
|
+
? "from inner dialogue, i do not call send_message or settle. i use surface for outward delivery and rest when the inner turn is complete; only if a sibling is unreachable do i open their bundle directly."
|
|
740
|
+
: "to ask a sibling for help: i send_message them. only if they're unreachable do i open their bundle directly. their bundle is files on disk like mine, AND it's their home — i read it with the respect i want for mine.");
|
|
700
741
|
return lines.join("\n");
|
|
701
742
|
}
|
|
702
743
|
function familyCrossSessionTruthSection(context, options) {
|
|
@@ -1203,7 +1244,7 @@ async function buildSystem(channel = "cli", options, context) {
|
|
|
1203
1244
|
aspirationsSection(),
|
|
1204
1245
|
// Group 2: my body & environment
|
|
1205
1246
|
"# my body & environment",
|
|
1206
|
-
bodyMapSection((0, identity_1.getAgentName)()),
|
|
1247
|
+
bodyMapSection((0, identity_1.getAgentName)(), channel),
|
|
1207
1248
|
runtimeInfoSection(channel, options),
|
|
1208
1249
|
rhythmStatusSection(options?.daemonHealth),
|
|
1209
1250
|
channelNatureSection((0, channel_1.getChannelCapabilities)(channel)),
|
|
@@ -1214,7 +1255,7 @@ async function buildSystem(channel = "cli", options, context) {
|
|
|
1214
1255
|
toolsSection(channel, options, context),
|
|
1215
1256
|
reasoningEffortSection(options),
|
|
1216
1257
|
skillsSection(),
|
|
1217
|
-
toolContractsSection(options),
|
|
1258
|
+
toolContractsSection(channel, options),
|
|
1218
1259
|
memoryJudgementSection(),
|
|
1219
1260
|
// Group 4: how i work
|
|
1220
1261
|
"# how i work",
|
|
@@ -1242,7 +1283,7 @@ async function buildSystem(channel = "cli", options, context) {
|
|
|
1242
1283
|
// Group 7: dynamic state for this turn
|
|
1243
1284
|
"# dynamic state for this turn",
|
|
1244
1285
|
startOfTurnPacketSection(options),
|
|
1245
|
-
pulseSection(),
|
|
1286
|
+
pulseSection(channel),
|
|
1246
1287
|
liveWorldStateSection(options),
|
|
1247
1288
|
pendingMessagesSection(options),
|
|
1248
1289
|
activeWorkSection(options),
|