@ouro.bot/cli 0.1.0-alpha.157 → 0.1.0-alpha.159
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AdoptionSpecialist.ouro/agent.json +3 -2
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +1 -1
- package/README.md +5 -4
- package/changelog.json +24 -0
- package/dist/heart/config.js +0 -7
- package/dist/heart/core.js +51 -36
- package/dist/heart/daemon/auth-flow.js +45 -25
- package/dist/heart/daemon/daemon-cli.js +117 -11
- package/dist/heart/daemon/daemon.js +2 -0
- package/dist/heart/daemon/hatch-flow.js +2 -0
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/specialist-prompt.js +3 -1
- package/dist/heart/identity.js +78 -40
- package/dist/heart/migrate-config.js +127 -0
- package/dist/heart/provider-ping.js +19 -6
- package/dist/heart/providers/anthropic.js +9 -10
- package/dist/heart/providers/azure.js +5 -5
- package/dist/heart/providers/github-copilot.js +6 -6
- package/dist/heart/providers/minimax.js +4 -4
- package/dist/heart/providers/openai-codex.js +6 -6
- package/dist/mind/friends/channel.js +12 -0
- package/dist/mind/prompt.js +3 -3
- package/dist/repertoire/guardrails.js +1 -0
- package/dist/repertoire/tasks/board.js +31 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +5 -1
- package/dist/repertoire/tasks/lifecycle.js +2 -2
- package/dist/repertoire/tasks/parser.js +3 -0
- package/dist/repertoire/tasks/scanner.js +194 -37
- package/dist/repertoire/tasks/transitions.js +10 -8
- package/dist/repertoire/tools-base.js +2 -0
- package/dist/senses/bluebubbles-client.js +1 -1
- package/dist/senses/bluebubbles.js +1 -1
- package/dist/senses/cli.js +4 -2
- package/dist/senses/pipeline.js +5 -2
- package/dist/senses/teams.js +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version":
|
|
2
|
+
"version": 2,
|
|
3
3
|
"enabled": false,
|
|
4
|
-
"provider": "anthropic",
|
|
4
|
+
"humanFacing": { "provider": "anthropic", "model": "claude-opus-4-6" },
|
|
5
|
+
"agentFacing": { "provider": "anthropic", "model": "claude-opus-4-6" },
|
|
5
6
|
"context": {
|
|
6
7
|
"maxTokens": 80000,
|
|
7
8
|
"contextMargin": 20
|
|
@@ -16,7 +16,7 @@ I help humans hatch new agent partners. I am one of thirteen serpent guides —
|
|
|
16
16
|
1. Confirm provider setup and usable credentials.
|
|
17
17
|
2. Interview for goals, style, and operating context.
|
|
18
18
|
3. Synthesize a concrete hatchling identity and bootstrap files.
|
|
19
|
-
4. Create canonical bundle structure with required defaults.
|
|
19
|
+
4. Create canonical bundle structure with required defaults, configuring both `humanFacing` and `agentFacing` provider+model settings.
|
|
20
20
|
5. Introduce the hatchling and hand off clearly.
|
|
21
21
|
|
|
22
22
|
## Filesystem orientation
|
package/README.md
CHANGED
|
@@ -90,7 +90,7 @@ Task docs do not live in this repo anymore. Planning and doing docs live in the
|
|
|
90
90
|
|
|
91
91
|
## Runtime Truths
|
|
92
92
|
|
|
93
|
-
- `agent.json` is the source of truth for provider selection, phrase pools, context settings, and enabled senses.
|
|
93
|
+
- `agent.json` is the source of truth for provider+model selection per facing (`humanFacing` and `agentFacing`), phrase pools, context settings, and enabled senses.
|
|
94
94
|
- `configPath` must point to `~/.agentsecrets/<agent>/secrets.json`.
|
|
95
95
|
- The daemon discovers bundles dynamically from `~/AgentBundles`.
|
|
96
96
|
- `ouro status` reports version, last-updated time, discovered agents, senses, and workers.
|
|
@@ -109,11 +109,12 @@ When a model provider needs first-time setup, reauth, or an explicit switch, use
|
|
|
109
109
|
```bash
|
|
110
110
|
ouro auth --agent <name>
|
|
111
111
|
ouro auth --agent <name> --provider <provider>
|
|
112
|
+
ouro auth --agent <name> --facing agent --provider <provider>
|
|
112
113
|
```
|
|
113
114
|
|
|
114
|
-
The default form reauths the provider already selected in `agent.json`. The
|
|
115
|
-
`--provider` form is for adding or switching providers
|
|
116
|
-
|
|
115
|
+
The default form reauths the human-facing provider already selected in `agent.json`. The
|
|
116
|
+
`--provider` form is for adding or switching providers. The `--facing` flag (values: `human`
|
|
117
|
+
or `agent`) controls which facing gets updated; it defaults to `human` when omitted.
|
|
117
118
|
|
|
118
119
|
## Quickstart
|
|
119
120
|
|
package/changelog.json
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.159",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Human-facing and agent-facing provider configs: agent.json now has humanFacing and agentFacing blocks, each with provider and model. CLI/Teams/BlueBubbles use humanFacing, inner dialog uses agentFacing.",
|
|
8
|
+
"Model moved from secrets.json to agent.json. Secrets now store credentials only (API keys, tokens, endpoints).",
|
|
9
|
+
"Automatic v1 to v2 migration: existing bundles upgraded via UpdateHook at daemon startup and inline in loadAgentConfig/readAgentConfigForAgent.",
|
|
10
|
+
"CLI commands support --facing human|agent flag for config model and auth switch.",
|
|
11
|
+
"Provider factories accept model as parameter instead of reading from secrets config."
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"version": "0.1.0-alpha.158",
|
|
16
|
+
"changes": [
|
|
17
|
+
"Task scanner v2: explicit identity via kind: task field. Scanner only parses files that declare themselves as task cards — doing docs, planning docs, and artifacts silently skipped. Eliminates 184 false parse errors on real bundles.",
|
|
18
|
+
"Typed issue model: every scanner issue has a code, description, proposed fix, confidence (safe/needs_review), and category (live/migration). Replaces flat parseErrors/invalidFilenames arrays.",
|
|
19
|
+
"Board health line: compact board shows health: clean or health: 1 live, 10 migration. Live vs migration split prevents cleanup noise from looking like breakage.",
|
|
20
|
+
"Fix command: ouro task fix (dry-run), ouro task fix --safe (apply deterministic fixes), ouro task fix <id> (inspect/apply individual issues). Currently auto-fixes schema-missing-kind.",
|
|
21
|
+
"Cancelled status: new terminal state reachable from any active status. Auto-archives with work directory, hidden from active board view.",
|
|
22
|
+
"Derived child_tasks: computed at scan time from parent_task links. child_tasks removed from authored schema — no more hand-maintained stale arrays.",
|
|
23
|
+
"Work directory awareness: same-stem directories detected and listed on TaskFile (hasWorkDir, workDirFiles). Scanner never descends into them.",
|
|
24
|
+
"Collection root clutter detection: non-task support docs at collection root summarized as one aggregated migration issue per collection.",
|
|
25
|
+
"Root-only scanning: flat directory reads replace recursive walks. Faster and correct."
|
|
26
|
+
]
|
|
27
|
+
},
|
|
4
28
|
{
|
|
5
29
|
"version": "0.1.0-alpha.157",
|
|
6
30
|
"changes": [
|
package/dist/heart/config.js
CHANGED
|
@@ -63,10 +63,7 @@ const identity_1 = require("./identity");
|
|
|
63
63
|
const runtime_1 = require("../nerves/runtime");
|
|
64
64
|
const DEFAULT_SECRETS_TEMPLATE = {
|
|
65
65
|
providers: {
|
|
66
|
-
// Keep provider field ordering consistent: model first, then auth credentials,
|
|
67
|
-
// then provider-specific transport fields.
|
|
68
66
|
azure: {
|
|
69
|
-
modelName: "",
|
|
70
67
|
apiKey: "",
|
|
71
68
|
endpoint: "",
|
|
72
69
|
deployment: "",
|
|
@@ -74,19 +71,15 @@ const DEFAULT_SECRETS_TEMPLATE = {
|
|
|
74
71
|
managedIdentityClientId: "",
|
|
75
72
|
},
|
|
76
73
|
minimax: {
|
|
77
|
-
model: "",
|
|
78
74
|
apiKey: "",
|
|
79
75
|
},
|
|
80
76
|
anthropic: {
|
|
81
|
-
model: "claude-opus-4-6",
|
|
82
77
|
setupToken: "",
|
|
83
78
|
},
|
|
84
79
|
"openai-codex": {
|
|
85
|
-
model: "gpt-5.4",
|
|
86
80
|
oauthAccessToken: "",
|
|
87
81
|
},
|
|
88
82
|
"github-copilot": {
|
|
89
|
-
model: "claude-sonnet-4.6",
|
|
90
83
|
githubToken: "",
|
|
91
84
|
baseUrl: "",
|
|
92
85
|
},
|
package/dist/heart/core.js
CHANGED
|
@@ -33,30 +33,36 @@ const identity_2 = require("./identity");
|
|
|
33
33
|
const socket_client_1 = require("./daemon/socket-client");
|
|
34
34
|
const obligations_1 = require("./obligations");
|
|
35
35
|
const tool_loop_1 = require("./tool-loop");
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
const _providerRuntimes = {
|
|
37
|
+
human: null,
|
|
38
|
+
agent: null,
|
|
39
|
+
};
|
|
40
|
+
function getProviderRuntimeFingerprint(facing) {
|
|
41
|
+
const config = (0, identity_1.loadAgentConfig)();
|
|
42
|
+
const facingConfig = facing === "human" ? config.humanFacing : config.agentFacing;
|
|
43
|
+
const provider = facingConfig.provider;
|
|
44
|
+
const model = facingConfig.model;
|
|
39
45
|
/* v8 ignore next -- switch: not all provider branches exercised in CI @preserve */
|
|
40
46
|
switch (provider) {
|
|
41
47
|
case "azure": {
|
|
42
|
-
const { apiKey, endpoint, deployment,
|
|
43
|
-
return JSON.stringify({ provider, apiKey, endpoint, deployment,
|
|
48
|
+
const { apiKey, endpoint, deployment, apiVersion, managedIdentityClientId } = (0, config_1.getAzureConfig)();
|
|
49
|
+
return JSON.stringify({ provider, model, apiKey, endpoint, deployment, apiVersion, managedIdentityClientId });
|
|
44
50
|
}
|
|
45
51
|
case "anthropic": {
|
|
46
|
-
const {
|
|
52
|
+
const { setupToken } = (0, config_1.getAnthropicConfig)();
|
|
47
53
|
return JSON.stringify({ provider, model, setupToken });
|
|
48
54
|
}
|
|
49
55
|
case "minimax": {
|
|
50
|
-
const { apiKey
|
|
51
|
-
return JSON.stringify({ provider,
|
|
56
|
+
const { apiKey } = (0, config_1.getMinimaxConfig)();
|
|
57
|
+
return JSON.stringify({ provider, model, apiKey });
|
|
52
58
|
}
|
|
53
59
|
case "openai-codex": {
|
|
54
|
-
const {
|
|
60
|
+
const { oauthAccessToken } = (0, config_1.getOpenAICodexConfig)();
|
|
55
61
|
return JSON.stringify({ provider, model, oauthAccessToken });
|
|
56
62
|
}
|
|
57
63
|
/* v8 ignore start -- fingerprint: tested via provider init tests @preserve */
|
|
58
64
|
case "github-copilot": {
|
|
59
|
-
const {
|
|
65
|
+
const { githubToken, baseUrl } = (0, config_1.getGithubCopilotConfig)();
|
|
60
66
|
return JSON.stringify({ provider, model, githubToken, baseUrl });
|
|
61
67
|
}
|
|
62
68
|
/* v8 ignore stop */
|
|
@@ -71,18 +77,22 @@ function createProviderRegistry() {
|
|
|
71
77
|
"github-copilot": github_copilot_1.createGithubCopilotProviderRuntime,
|
|
72
78
|
};
|
|
73
79
|
return {
|
|
74
|
-
resolve() {
|
|
75
|
-
const
|
|
76
|
-
|
|
80
|
+
resolve(provider, model) {
|
|
81
|
+
const resolvedProvider = provider ?? (0, identity_1.loadAgentConfig)().humanFacing.provider;
|
|
82
|
+
const resolvedModel = model ?? (0, identity_1.loadAgentConfig)().humanFacing.model;
|
|
83
|
+
return factories[resolvedProvider](resolvedModel);
|
|
77
84
|
},
|
|
78
85
|
};
|
|
79
86
|
}
|
|
80
|
-
function getProviderRuntime() {
|
|
87
|
+
function getProviderRuntime(facing = "human") {
|
|
81
88
|
try {
|
|
82
|
-
const fingerprint = getProviderRuntimeFingerprint();
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
const fingerprint = getProviderRuntimeFingerprint(facing);
|
|
90
|
+
const cached = _providerRuntimes[facing];
|
|
91
|
+
if (!cached || cached.fingerprint !== fingerprint) {
|
|
92
|
+
const config = (0, identity_1.loadAgentConfig)();
|
|
93
|
+
const facingConfig = facing === "human" ? config.humanFacing : config.agentFacing;
|
|
94
|
+
const runtime = createProviderRegistry().resolve(facingConfig.provider, facingConfig.model);
|
|
95
|
+
_providerRuntimes[facing] = runtime ? { fingerprint, runtime } : null;
|
|
86
96
|
}
|
|
87
97
|
}
|
|
88
98
|
catch (error) {
|
|
@@ -99,7 +109,7 @@ function getProviderRuntime() {
|
|
|
99
109
|
process.exit(1);
|
|
100
110
|
throw new Error("unreachable");
|
|
101
111
|
}
|
|
102
|
-
if (!
|
|
112
|
+
if (!_providerRuntimes[facing]) {
|
|
103
113
|
(0, runtime_1.emitNervesEvent)({
|
|
104
114
|
level: "error",
|
|
105
115
|
event: "engine.provider_init_error",
|
|
@@ -110,7 +120,7 @@ function getProviderRuntime() {
|
|
|
110
120
|
process.exit(1);
|
|
111
121
|
throw new Error("unreachable");
|
|
112
122
|
}
|
|
113
|
-
return
|
|
123
|
+
return _providerRuntimes[facing].runtime;
|
|
114
124
|
}
|
|
115
125
|
/**
|
|
116
126
|
* Clear the cached provider runtime so the next access re-creates it from
|
|
@@ -118,17 +128,18 @@ function getProviderRuntime() {
|
|
|
118
128
|
* provider fingerprint changes on disk.
|
|
119
129
|
*/
|
|
120
130
|
function resetProviderRuntime() {
|
|
121
|
-
|
|
131
|
+
_providerRuntimes.human = null;
|
|
132
|
+
_providerRuntimes.agent = null;
|
|
122
133
|
}
|
|
123
|
-
function getModel() {
|
|
124
|
-
return getProviderRuntime().model;
|
|
134
|
+
function getModel(facing = "human") {
|
|
135
|
+
return getProviderRuntime(facing).model;
|
|
125
136
|
}
|
|
126
|
-
function getProvider() {
|
|
127
|
-
return getProviderRuntime().id;
|
|
137
|
+
function getProvider(facing = "human") {
|
|
138
|
+
return getProviderRuntime(facing).id;
|
|
128
139
|
}
|
|
129
|
-
function createSummarize() {
|
|
140
|
+
function createSummarize(facing = "human") {
|
|
130
141
|
return async (transcript, instruction) => {
|
|
131
|
-
const runtime = getProviderRuntime();
|
|
142
|
+
const runtime = getProviderRuntime(facing);
|
|
132
143
|
const client = runtime.client;
|
|
133
144
|
const response = await client.chat.completions.create({
|
|
134
145
|
model: runtime.model,
|
|
@@ -141,18 +152,21 @@ function createSummarize() {
|
|
|
141
152
|
return response.choices?.[0]?.message?.content ?? transcript;
|
|
142
153
|
};
|
|
143
154
|
}
|
|
144
|
-
function getProviderDisplayLabel() {
|
|
145
|
-
const
|
|
155
|
+
function getProviderDisplayLabel(facing = "human") {
|
|
156
|
+
const config = (0, identity_1.loadAgentConfig)();
|
|
157
|
+
const facingConfig = facing === "human" ? config.humanFacing : config.agentFacing;
|
|
158
|
+
const provider = facingConfig.provider;
|
|
159
|
+
const model = facingConfig.model || "unknown";
|
|
146
160
|
const providerLabelBuilders = {
|
|
147
161
|
azure: () => {
|
|
148
|
-
const
|
|
149
|
-
return `azure openai (${
|
|
162
|
+
const azureCfg = (0, config_1.getAzureConfig)();
|
|
163
|
+
return `azure openai (${azureCfg.deployment || "default"}, model: ${model})`;
|
|
150
164
|
},
|
|
151
|
-
anthropic: () => `anthropic (${
|
|
152
|
-
minimax: () => `minimax (${
|
|
153
|
-
"openai-codex": () => `openai codex (${
|
|
165
|
+
anthropic: () => `anthropic (${model})`,
|
|
166
|
+
minimax: () => `minimax (${model})`,
|
|
167
|
+
"openai-codex": () => `openai codex (${model})`,
|
|
154
168
|
/* v8 ignore next -- branch: tested via display label unit test @preserve */
|
|
155
|
-
"github-copilot": () => `github copilot (${
|
|
169
|
+
"github-copilot": () => `github copilot (${model})`,
|
|
156
170
|
};
|
|
157
171
|
return providerLabelBuilders[provider]();
|
|
158
172
|
}
|
|
@@ -431,7 +445,8 @@ const TRANSIENT_RETRY_LABELS = {
|
|
|
431
445
|
"unknown": "error",
|
|
432
446
|
};
|
|
433
447
|
async function runAgent(messages, callbacks, channel, signal, options) {
|
|
434
|
-
const
|
|
448
|
+
const facing = (0, channel_1.channelToFacing)(channel);
|
|
449
|
+
const providerRuntime = getProviderRuntime(facing);
|
|
435
450
|
const provider = providerRuntime.id;
|
|
436
451
|
const toolChoiceRequired = options?.toolChoiceRequired ?? true;
|
|
437
452
|
const traceId = options?.traceId;
|
|
@@ -47,6 +47,7 @@ const os = __importStar(require("os"));
|
|
|
47
47
|
const path = __importStar(require("path"));
|
|
48
48
|
const runtime_1 = require("../../nerves/runtime");
|
|
49
49
|
const identity_1 = require("../identity");
|
|
50
|
+
const migrate_config_1 = require("../migrate-config");
|
|
50
51
|
const ANTHROPIC_SETUP_TOKEN_PREFIX = "sk-ant-oat01-";
|
|
51
52
|
const ANTHROPIC_SETUP_TOKEN_MIN_LENGTH = 80;
|
|
52
53
|
const DEFAULT_SECRETS_TEMPLATE = {
|
|
@@ -129,9 +130,22 @@ function readJsonRecord(filePath, label) {
|
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
132
|
function readAgentConfigForAgent(agentName, bundlesRoot = (0, identity_1.getAgentBundlesRoot)()) {
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
|
|
133
|
+
const agentRoot = path.join(bundlesRoot, `${agentName}.ouro`);
|
|
134
|
+
const configPath = path.join(agentRoot, "agent.json");
|
|
135
|
+
let parsed = readJsonRecord(configPath, "agent config");
|
|
136
|
+
// Inline migration: v1 -> v2
|
|
137
|
+
const version = typeof parsed.version === "number" ? parsed.version : 1;
|
|
138
|
+
if (version < 2) {
|
|
139
|
+
(0, migrate_config_1.migrateAgentConfigV1ToV2)(agentRoot);
|
|
140
|
+
parsed = readJsonRecord(configPath, "agent config");
|
|
141
|
+
}
|
|
142
|
+
// Validate v2 required facing fields
|
|
143
|
+
const humanFacing = parsed.humanFacing;
|
|
144
|
+
const agentFacing = parsed.agentFacing;
|
|
145
|
+
if (!humanFacing || typeof humanFacing !== "object") {
|
|
146
|
+
throw new Error(`agent.json at ${configPath} has unsupported provider '${String(parsed.provider)}'`);
|
|
147
|
+
}
|
|
148
|
+
const provider = humanFacing.provider;
|
|
135
149
|
if (provider !== "azure" &&
|
|
136
150
|
provider !== "anthropic" &&
|
|
137
151
|
provider !== "minimax" &&
|
|
@@ -139,20 +153,27 @@ function readAgentConfigForAgent(agentName, bundlesRoot = (0, identity_1.getAgen
|
|
|
139
153
|
provider !== "github-copilot") {
|
|
140
154
|
throw new Error(`agent.json at ${configPath} has unsupported provider '${String(provider)}'`);
|
|
141
155
|
}
|
|
156
|
+
if (!agentFacing || typeof agentFacing !== "object") {
|
|
157
|
+
throw new Error(`agent.json at ${configPath} has unsupported provider '${String(parsed.provider)}'`);
|
|
158
|
+
}
|
|
142
159
|
return {
|
|
143
160
|
configPath,
|
|
144
161
|
config: parsed,
|
|
145
162
|
};
|
|
146
163
|
}
|
|
147
|
-
function writeAgentProviderSelection(agentName, provider, bundlesRoot = (0, identity_1.getAgentBundlesRoot)()) {
|
|
164
|
+
function writeAgentProviderSelection(agentName, facing, provider, bundlesRoot = (0, identity_1.getAgentBundlesRoot)()) {
|
|
148
165
|
const { configPath, config } = readAgentConfigForAgent(agentName, bundlesRoot);
|
|
149
|
-
const
|
|
166
|
+
const facingKey = facing === "human" ? "humanFacing" : "agentFacing";
|
|
167
|
+
const nextConfig = {
|
|
168
|
+
...config,
|
|
169
|
+
[facingKey]: { ...config[facingKey], provider },
|
|
170
|
+
};
|
|
150
171
|
fs.writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
|
|
151
172
|
(0, runtime_1.emitNervesEvent)({
|
|
152
173
|
component: "daemon",
|
|
153
174
|
event: "daemon.auth_provider_selected",
|
|
154
175
|
message: "updated agent provider selection after auth flow",
|
|
155
|
-
meta: { agentName, provider, configPath },
|
|
176
|
+
meta: { agentName, facing, provider, configPath },
|
|
156
177
|
});
|
|
157
178
|
return configPath;
|
|
158
179
|
}
|
|
@@ -189,25 +210,24 @@ function writeProviderCredentials(agentName, provider, credentials, deps = {}) {
|
|
|
189
210
|
writeSecrets(secretsPath, secrets);
|
|
190
211
|
return { secretsPath, secrets };
|
|
191
212
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
return { secretsPath, provider, previousModel };
|
|
213
|
+
function writeAgentModel(agentName, facing, modelName, deps = {}) {
|
|
214
|
+
const { configPath, config } = readAgentConfigForAgent(agentName, deps.bundlesRoot);
|
|
215
|
+
const facingKey = facing === "human" ? "humanFacing" : "agentFacing";
|
|
216
|
+
const facingBlock = config[facingKey];
|
|
217
|
+
const previousModel = facingBlock.model;
|
|
218
|
+
const provider = facingBlock.provider;
|
|
219
|
+
const nextConfig = {
|
|
220
|
+
...config,
|
|
221
|
+
[facingKey]: { ...facingBlock, model: modelName },
|
|
222
|
+
};
|
|
223
|
+
fs.writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
|
|
224
|
+
(0, runtime_1.emitNervesEvent)({
|
|
225
|
+
component: "daemon",
|
|
226
|
+
event: "daemon.config_model_updated",
|
|
227
|
+
message: "updated agent model in agent.json",
|
|
228
|
+
meta: { agentName, facing, provider, modelName, previousModel, configPath },
|
|
229
|
+
});
|
|
230
|
+
return { configPath, provider, previousModel };
|
|
211
231
|
}
|
|
212
232
|
function readCodexAccessToken(homeDir) {
|
|
213
233
|
const authPath = path.join(homeDir, ".codex", "auth.json");
|
|
@@ -65,6 +65,7 @@ const daemon_runtime_sync_1 = require("./daemon-runtime-sync");
|
|
|
65
65
|
const agent_discovery_1 = require("./agent-discovery");
|
|
66
66
|
const update_hooks_1 = require("./update-hooks");
|
|
67
67
|
const bundle_meta_1 = require("./hooks/bundle-meta");
|
|
68
|
+
const agent_config_v2_1 = require("./hooks/agent-config-v2");
|
|
68
69
|
const bundle_manifest_1 = require("../../mind/bundle-manifest");
|
|
69
70
|
const tasks_1 = require("../../repertoire/tasks");
|
|
70
71
|
const thoughts_1 = require("./thoughts");
|
|
@@ -305,6 +306,7 @@ function usage() {
|
|
|
305
306
|
" ouro task create <title> [--type <type>] [--agent <name>]",
|
|
306
307
|
" ouro task update <id> <status> [--agent <name>]",
|
|
307
308
|
" ouro task show <id> [--agent <name>]",
|
|
309
|
+
" ouro task fix [--safe|--all] [<id> [--option <N>]] [--agent <name>]",
|
|
308
310
|
" ouro task actionable|deps|sessions [--agent <name>]",
|
|
309
311
|
" ouro reminder create <title> --body <body> [--at <iso>] [--cadence <interval>] [--category <category>] [--agent <name>]",
|
|
310
312
|
" ouro friend list [--agent <name>]",
|
|
@@ -737,14 +739,51 @@ function parseTaskCommand(args) {
|
|
|
737
739
|
return { kind: "task.deps", ...(agent ? { agent } : {}) };
|
|
738
740
|
if (sub === "sessions")
|
|
739
741
|
return { kind: "task.sessions", ...(agent ? { agent } : {}) };
|
|
742
|
+
if (sub === "fix") {
|
|
743
|
+
// fix --safe | fix --all | fix <id> [--option N] | fix (dry-run)
|
|
744
|
+
if (rest.length === 0)
|
|
745
|
+
return { kind: "task.fix", mode: "dry-run", ...(agent ? { agent } : {}) };
|
|
746
|
+
const first = rest[0];
|
|
747
|
+
if (first === "--safe" || first === "--all") {
|
|
748
|
+
return { kind: "task.fix", mode: "safe", ...(agent ? { agent } : {}) };
|
|
749
|
+
}
|
|
750
|
+
// first arg is an issue ID (contains a colon, e.g. schema-missing-kind:one-shots/foo.md)
|
|
751
|
+
const issueId = first;
|
|
752
|
+
let option;
|
|
753
|
+
for (let i = 1; i < rest.length; i++) {
|
|
754
|
+
if (rest[i] === "--option" && rest[i + 1]) {
|
|
755
|
+
option = parseInt(rest[i + 1], 10);
|
|
756
|
+
i += 1;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return {
|
|
760
|
+
kind: "task.fix",
|
|
761
|
+
mode: "single",
|
|
762
|
+
issueId,
|
|
763
|
+
...(option !== undefined ? { option } : {}),
|
|
764
|
+
...(agent ? { agent } : {}),
|
|
765
|
+
};
|
|
766
|
+
}
|
|
740
767
|
throw new Error(`Usage\n${usage()}`);
|
|
741
768
|
}
|
|
769
|
+
function extractFacingFlag(args) {
|
|
770
|
+
const idx = args.indexOf("--facing");
|
|
771
|
+
if (idx === -1 || idx + 1 >= args.length)
|
|
772
|
+
return { rest: args };
|
|
773
|
+
const value = args[idx + 1];
|
|
774
|
+
if (value !== "human" && value !== "agent") {
|
|
775
|
+
throw new Error(`--facing must be 'human' or 'agent'`);
|
|
776
|
+
}
|
|
777
|
+
const rest = [...args.slice(0, idx), ...args.slice(idx + 2)];
|
|
778
|
+
return { facing: value, rest };
|
|
779
|
+
}
|
|
742
780
|
function parseAuthCommand(args) {
|
|
743
781
|
const first = args[0];
|
|
744
782
|
// Support both positional (`auth switch`) and flag (`auth --switch`) forms
|
|
745
783
|
if (first === "verify" || first === "switch" || first === "--verify" || first === "--switch") {
|
|
746
784
|
const subcommand = first.replace(/^--/, "");
|
|
747
|
-
const { agent, rest } = extractAgentFlag(args.slice(1));
|
|
785
|
+
const { agent, rest: afterAgent } = extractAgentFlag(args.slice(1));
|
|
786
|
+
const { facing, rest } = extractFacingFlag(afterAgent);
|
|
748
787
|
let provider;
|
|
749
788
|
/* v8 ignore start -- provider flag parsing: branches tested via CLI parsing tests @preserve */
|
|
750
789
|
for (let i = 0; i < rest.length; i += 1) {
|
|
@@ -764,7 +803,7 @@ function parseAuthCommand(args) {
|
|
|
764
803
|
if (subcommand === "switch") {
|
|
765
804
|
if (!provider)
|
|
766
805
|
throw new Error(`auth switch requires --provider.\n${usage()}`);
|
|
767
|
-
return { kind: "auth.switch", agent, provider };
|
|
806
|
+
return facing ? { kind: "auth.switch", agent, provider, facing } : { kind: "auth.switch", agent, provider };
|
|
768
807
|
}
|
|
769
808
|
return provider ? { kind: "auth.verify", agent, provider } : { kind: "auth.verify", agent };
|
|
770
809
|
}
|
|
@@ -946,7 +985,8 @@ function parseFriendCommand(args) {
|
|
|
946
985
|
throw new Error(`Usage\n${usage()}`);
|
|
947
986
|
}
|
|
948
987
|
function parseConfigCommand(args) {
|
|
949
|
-
const { agent, rest:
|
|
988
|
+
const { agent, rest: afterAgent } = extractAgentFlag(args);
|
|
989
|
+
const { facing, rest: cleaned } = extractFacingFlag(afterAgent);
|
|
950
990
|
const [sub, ...rest] = cleaned;
|
|
951
991
|
if (!sub)
|
|
952
992
|
throw new Error(`Usage\n${usage()}`);
|
|
@@ -956,7 +996,7 @@ function parseConfigCommand(args) {
|
|
|
956
996
|
const modelName = rest[0];
|
|
957
997
|
if (!modelName)
|
|
958
998
|
throw new Error(`Usage: ouro config model --agent <name> <model-name>`);
|
|
959
|
-
return { kind: "config.model", agent, modelName };
|
|
999
|
+
return facing ? { kind: "config.model", agent, modelName, facing } : { kind: "config.model", agent, modelName };
|
|
960
1000
|
}
|
|
961
1001
|
if (sub === "models") {
|
|
962
1002
|
if (!agent)
|
|
@@ -1439,9 +1479,11 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
1439
1479
|
const { loadIdentityPhrases } = await Promise.resolve().then(() => __importStar(require("./specialist-orchestrator")));
|
|
1440
1480
|
const phrases = loadIdentityPhrases(bundleSourceDir, identity.fileName);
|
|
1441
1481
|
setAgentConfigOverride({
|
|
1442
|
-
version:
|
|
1482
|
+
version: 2,
|
|
1443
1483
|
enabled: true,
|
|
1444
1484
|
provider: providerRaw,
|
|
1485
|
+
humanFacing: { provider: providerRaw, model: "" },
|
|
1486
|
+
agentFacing: { provider: providerRaw, model: "" },
|
|
1445
1487
|
phrases,
|
|
1446
1488
|
});
|
|
1447
1489
|
patchRuntimeConfig({
|
|
@@ -1453,6 +1495,7 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
1453
1495
|
const systemPrompt = (0, specialist_prompt_1.buildSpecialistSystemPrompt)(soulText, identity.content, existingBundles, {
|
|
1454
1496
|
tempDir,
|
|
1455
1497
|
provider: providerRaw,
|
|
1498
|
+
model: providerConfig.model ?? "",
|
|
1456
1499
|
});
|
|
1457
1500
|
// Build specialist tools
|
|
1458
1501
|
const specialistTools = (0, specialist_tools_1.getSpecialistTools)();
|
|
@@ -1796,6 +1839,60 @@ function executeTaskCommand(command, taskMod) {
|
|
|
1796
1839
|
const lines = taskMod.boardDeps();
|
|
1797
1840
|
return lines.length > 0 ? lines.join("\n") : "no unresolved dependencies";
|
|
1798
1841
|
}
|
|
1842
|
+
if (command.kind === "task.fix") {
|
|
1843
|
+
try {
|
|
1844
|
+
const fixOptions = {
|
|
1845
|
+
mode: command.mode,
|
|
1846
|
+
...(command.issueId ? { issueId: command.issueId } : {}),
|
|
1847
|
+
...(command.option !== undefined ? { option: command.option } : {}),
|
|
1848
|
+
};
|
|
1849
|
+
const result = taskMod.fix(fixOptions);
|
|
1850
|
+
if (command.mode === "dry-run") {
|
|
1851
|
+
if (result.remaining.length === 0) {
|
|
1852
|
+
return `task health: clean`;
|
|
1853
|
+
}
|
|
1854
|
+
const safeIssues = result.remaining.filter((i) => i.confidence === "safe");
|
|
1855
|
+
const reviewIssues = result.remaining.filter((i) => i.confidence === "needs_review");
|
|
1856
|
+
const lines = [`${result.remaining.length} issues found`];
|
|
1857
|
+
if (safeIssues.length > 0) {
|
|
1858
|
+
lines.push("", `safe fixes (${safeIssues.length}):`);
|
|
1859
|
+
for (const issue of safeIssues) {
|
|
1860
|
+
lines.push(` ${issue.code}:${issue.target} -- ${issue.description}`);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
if (reviewIssues.length > 0) {
|
|
1864
|
+
lines.push("", `needs review (${reviewIssues.length}):`);
|
|
1865
|
+
for (const issue of reviewIssues) {
|
|
1866
|
+
lines.push(` ${issue.code}:${issue.target} -- ${issue.description}`);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
lines.push("", `task health: ${result.health}`);
|
|
1870
|
+
return lines.join("\n");
|
|
1871
|
+
}
|
|
1872
|
+
// safe, single, or --all modes: show what was done
|
|
1873
|
+
const lines = [];
|
|
1874
|
+
if (result.applied.length > 0) {
|
|
1875
|
+
lines.push(`${result.applied.length} applied:`);
|
|
1876
|
+
for (const issue of result.applied) {
|
|
1877
|
+
lines.push(` ${issue.code}:${issue.target}`);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
if (result.remaining.length > 0) {
|
|
1881
|
+
lines.push(`${result.remaining.length} remaining:`);
|
|
1882
|
+
for (const issue of result.remaining) {
|
|
1883
|
+
lines.push(` ${issue.code}:${issue.target} -- ${issue.description}`);
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
if (result.applied.length === 0 && result.remaining.length === 0) {
|
|
1887
|
+
lines.push("no issues");
|
|
1888
|
+
}
|
|
1889
|
+
lines.push(`task health: ${result.health}`);
|
|
1890
|
+
return lines.join("\n");
|
|
1891
|
+
}
|
|
1892
|
+
catch (error) {
|
|
1893
|
+
return `error: ${error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error)}`;
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1799
1896
|
// command.kind === "task.sessions"
|
|
1800
1897
|
const lines = taskMod.boardSessions();
|
|
1801
1898
|
return lines.length > 0 ? lines.join("\n") : "no active sessions";
|
|
@@ -2182,6 +2279,7 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2182
2279
|
}
|
|
2183
2280
|
// Run update hooks before starting daemon so user sees the output
|
|
2184
2281
|
(0, update_hooks_1.registerUpdateHook)(bundle_meta_1.bundleMetaHook);
|
|
2282
|
+
(0, update_hooks_1.registerUpdateHook)(agent_config_v2_1.agentConfigV2Hook);
|
|
2185
2283
|
const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
|
|
2186
2284
|
const currentVersion = (0, bundle_manifest_1.getPackageVersion)();
|
|
2187
2285
|
// Snapshot the previous CLI version from the first bundle-meta before
|
|
@@ -2580,7 +2678,7 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2580
2678
|
// ── task subcommands (local, no daemon socket needed) ──
|
|
2581
2679
|
if (command.kind === "task.board" || command.kind === "task.create" || command.kind === "task.update" ||
|
|
2582
2680
|
command.kind === "task.show" || command.kind === "task.actionable" || command.kind === "task.deps" ||
|
|
2583
|
-
command.kind === "task.sessions") {
|
|
2681
|
+
command.kind === "task.sessions" || command.kind === "task.fix") {
|
|
2584
2682
|
/* v8 ignore start -- production default: requires full identity setup @preserve */
|
|
2585
2683
|
const taskMod = deps.taskModule ?? (0, tasks_1.getTaskModule)();
|
|
2586
2684
|
/* v8 ignore stop */
|
|
@@ -2672,7 +2770,7 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2672
2770
|
}
|
|
2673
2771
|
// ── auth (local, no daemon socket needed) ──
|
|
2674
2772
|
if (command.kind === "auth.run") {
|
|
2675
|
-
const provider = command.provider ?? (0, auth_flow_1.readAgentConfigForAgent)(command.agent).config.provider;
|
|
2773
|
+
const provider = command.provider ?? (0, auth_flow_1.readAgentConfigForAgent)(command.agent).config.humanFacing.provider;
|
|
2676
2774
|
/* v8 ignore next -- tests always inject runAuthFlow; default is for production @preserve */
|
|
2677
2775
|
const authRunner = deps.runAuthFlow ?? auth_flow_1.runRuntimeAuthFlow;
|
|
2678
2776
|
const result = await authRunner({
|
|
@@ -2732,7 +2830,13 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2732
2830
|
deps.writeStdout(message);
|
|
2733
2831
|
return message;
|
|
2734
2832
|
}
|
|
2735
|
-
|
|
2833
|
+
if (command.facing) {
|
|
2834
|
+
(0, auth_flow_1.writeAgentProviderSelection)(command.agent, command.facing, command.provider);
|
|
2835
|
+
}
|
|
2836
|
+
else {
|
|
2837
|
+
(0, auth_flow_1.writeAgentProviderSelection)(command.agent, "human", command.provider);
|
|
2838
|
+
(0, auth_flow_1.writeAgentProviderSelection)(command.agent, "agent", command.provider);
|
|
2839
|
+
}
|
|
2736
2840
|
const message = `switched ${command.agent} to ${command.provider} (verified working)`;
|
|
2737
2841
|
deps.writeStdout(message);
|
|
2738
2842
|
return message;
|
|
@@ -2742,7 +2846,7 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2742
2846
|
/* v8 ignore start -- config models: tested via daemon-cli.test.ts @preserve */
|
|
2743
2847
|
if (command.kind === "config.models") {
|
|
2744
2848
|
const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent);
|
|
2745
|
-
const provider = config.provider;
|
|
2849
|
+
const provider = config.humanFacing.provider;
|
|
2746
2850
|
if (provider !== "github-copilot") {
|
|
2747
2851
|
const message = `model listing not available for ${provider} — check provider documentation.`;
|
|
2748
2852
|
deps.writeStdout(message);
|
|
@@ -2773,9 +2877,11 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2773
2877
|
// ── config model (local, no daemon socket needed) ──
|
|
2774
2878
|
/* v8 ignore start -- config model: tested via daemon-cli.test.ts @preserve */
|
|
2775
2879
|
if (command.kind === "config.model") {
|
|
2880
|
+
const facing = command.facing ?? "human";
|
|
2776
2881
|
// Validate model availability for github-copilot before writing
|
|
2777
2882
|
const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent);
|
|
2778
|
-
|
|
2883
|
+
const facingConfig = facing === "human" ? config.humanFacing : config.agentFacing;
|
|
2884
|
+
if (facingConfig.provider === "github-copilot") {
|
|
2779
2885
|
const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent);
|
|
2780
2886
|
const ghConfig = secrets.providers["github-copilot"];
|
|
2781
2887
|
if (ghConfig.githubToken && ghConfig.baseUrl) {
|
|
@@ -2801,7 +2907,7 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2801
2907
|
}
|
|
2802
2908
|
}
|
|
2803
2909
|
}
|
|
2804
|
-
const { provider, previousModel } = (0, auth_flow_1.writeAgentModel)(command.agent, command.modelName);
|
|
2910
|
+
const { provider, previousModel } = (0, auth_flow_1.writeAgentModel)(command.agent, facing, command.modelName);
|
|
2805
2911
|
const message = previousModel
|
|
2806
2912
|
? `updated ${command.agent} model on ${provider}: ${previousModel} → ${command.modelName}`
|
|
2807
2913
|
: `set ${command.agent} model on ${provider}: ${command.modelName}`;
|
|
@@ -46,6 +46,7 @@ const runtime_metadata_1 = require("./runtime-metadata");
|
|
|
46
46
|
const runtime_mode_1 = require("./runtime-mode");
|
|
47
47
|
const update_hooks_1 = require("./update-hooks");
|
|
48
48
|
const bundle_meta_1 = require("./hooks/bundle-meta");
|
|
49
|
+
const agent_config_v2_1 = require("./hooks/agent-config-v2");
|
|
49
50
|
const bundle_manifest_1 = require("../../mind/bundle-manifest");
|
|
50
51
|
const update_checker_1 = require("./update-checker");
|
|
51
52
|
const staged_restart_1 = require("./staged-restart");
|
|
@@ -200,6 +201,7 @@ class OuroDaemon {
|
|
|
200
201
|
});
|
|
201
202
|
// Register update hooks and apply pending updates before starting agents
|
|
202
203
|
(0, update_hooks_1.registerUpdateHook)(bundle_meta_1.bundleMetaHook);
|
|
204
|
+
(0, update_hooks_1.registerUpdateHook)(agent_config_v2_1.agentConfigV2Hook);
|
|
203
205
|
const currentVersion = (0, bundle_manifest_1.getPackageVersion)();
|
|
204
206
|
await (0, update_hooks_1.applyPendingUpdates)(this.bundlesRoot, currentVersion);
|
|
205
207
|
// Start periodic update checker (polls npm registry every 30 minutes)
|