@ouro.bot/cli 0.1.0-alpha.69 → 0.1.0-alpha.70
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 +9 -0
- package/dist/heart/config.js +11 -0
- package/dist/heart/core.js +11 -0
- package/dist/heart/daemon/auth-flow.js +59 -1
- package/dist/heart/daemon/daemon-cli.js +124 -8
- package/dist/heart/daemon/hatch-flow.js +3 -0
- package/dist/heart/identity.js +3 -2
- package/dist/heart/model-capabilities.js +8 -0
- package/dist/heart/providers/github-copilot.js +149 -0
- package/dist/mind/prompt.js +3 -0
- package/dist/repertoire/guardrails.js +3 -0
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
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.70",
|
|
6
|
+
"changes": [
|
|
7
|
+
"New github-copilot LLM provider: use GitHub Copilot as model backend via `gh auth` token. Claude models route to /chat/completions, GPT models to /responses. Endpoint auto-discovered from your Copilot enterprise plan.",
|
|
8
|
+
"Auth decoupled from switching: `ouro auth --provider X` now stores credentials only. Use `ouro auth switch --provider X` to change the active provider, and `ouro auth verify` to check which stored providers have valid credentials.",
|
|
9
|
+
"Agents can now manage their own auth: `ouro auth`, `ouro auth verify`, and `ouro auth switch` are in the system prompt and gated at family trust level. Auth error messages include actionable recovery guidance.",
|
|
10
|
+
"Fix: `ouro --agent <name> <command>` now works correctly when --agent is the first argument."
|
|
11
|
+
]
|
|
12
|
+
},
|
|
4
13
|
{
|
|
5
14
|
"version": "0.1.0-alpha.69",
|
|
6
15
|
"changes": [
|
package/dist/heart/config.js
CHANGED
|
@@ -40,6 +40,7 @@ exports.getAzureConfig = getAzureConfig;
|
|
|
40
40
|
exports.getMinimaxConfig = getMinimaxConfig;
|
|
41
41
|
exports.getAnthropicConfig = getAnthropicConfig;
|
|
42
42
|
exports.getOpenAICodexConfig = getOpenAICodexConfig;
|
|
43
|
+
exports.getGithubCopilotConfig = getGithubCopilotConfig;
|
|
43
44
|
exports.getTeamsConfig = getTeamsConfig;
|
|
44
45
|
exports.getTeamsSecondaryConfig = getTeamsSecondaryConfig;
|
|
45
46
|
exports.getContextConfig = getContextConfig;
|
|
@@ -84,6 +85,11 @@ const DEFAULT_SECRETS_TEMPLATE = {
|
|
|
84
85
|
model: "gpt-5.4",
|
|
85
86
|
oauthAccessToken: "",
|
|
86
87
|
},
|
|
88
|
+
"github-copilot": {
|
|
89
|
+
model: "claude-sonnet-4.6",
|
|
90
|
+
githubToken: "",
|
|
91
|
+
baseUrl: "",
|
|
92
|
+
},
|
|
87
93
|
},
|
|
88
94
|
teams: {
|
|
89
95
|
clientId: "",
|
|
@@ -128,6 +134,7 @@ function defaultRuntimeConfig() {
|
|
|
128
134
|
minimax: { ...DEFAULT_SECRETS_TEMPLATE.providers.minimax },
|
|
129
135
|
anthropic: { ...DEFAULT_SECRETS_TEMPLATE.providers.anthropic },
|
|
130
136
|
"openai-codex": { ...DEFAULT_SECRETS_TEMPLATE.providers["openai-codex"] },
|
|
137
|
+
"github-copilot": { ...DEFAULT_SECRETS_TEMPLATE.providers["github-copilot"] },
|
|
131
138
|
},
|
|
132
139
|
teams: { ...DEFAULT_SECRETS_TEMPLATE.teams },
|
|
133
140
|
teamsSecondary: { ...DEFAULT_SECRETS_TEMPLATE.teamsSecondary },
|
|
@@ -265,6 +272,10 @@ function getOpenAICodexConfig() {
|
|
|
265
272
|
const config = loadConfig();
|
|
266
273
|
return { ...config.providers["openai-codex"] };
|
|
267
274
|
}
|
|
275
|
+
function getGithubCopilotConfig() {
|
|
276
|
+
const config = loadConfig();
|
|
277
|
+
return { ...config.providers["github-copilot"] };
|
|
278
|
+
}
|
|
268
279
|
function getTeamsConfig() {
|
|
269
280
|
const config = loadConfig();
|
|
270
281
|
return { ...config.teams };
|
package/dist/heart/core.js
CHANGED
|
@@ -24,9 +24,11 @@ const anthropic_1 = require("./providers/anthropic");
|
|
|
24
24
|
const azure_1 = require("./providers/azure");
|
|
25
25
|
const minimax_1 = require("./providers/minimax");
|
|
26
26
|
const openai_codex_1 = require("./providers/openai-codex");
|
|
27
|
+
const github_copilot_1 = require("./providers/github-copilot");
|
|
27
28
|
let _providerRuntime = null;
|
|
28
29
|
function getProviderRuntimeFingerprint() {
|
|
29
30
|
const provider = (0, identity_1.loadAgentConfig)().provider;
|
|
31
|
+
/* v8 ignore next -- switch: not all provider branches exercised in CI @preserve */
|
|
30
32
|
switch (provider) {
|
|
31
33
|
case "azure": {
|
|
32
34
|
const { apiKey, endpoint, deployment, modelName, apiVersion, managedIdentityClientId } = (0, config_1.getAzureConfig)();
|
|
@@ -44,6 +46,12 @@ function getProviderRuntimeFingerprint() {
|
|
|
44
46
|
const { model, oauthAccessToken } = (0, config_1.getOpenAICodexConfig)();
|
|
45
47
|
return JSON.stringify({ provider, model, oauthAccessToken });
|
|
46
48
|
}
|
|
49
|
+
/* v8 ignore start -- fingerprint: tested via provider init tests @preserve */
|
|
50
|
+
case "github-copilot": {
|
|
51
|
+
const { model, githubToken, baseUrl } = (0, config_1.getGithubCopilotConfig)();
|
|
52
|
+
return JSON.stringify({ provider, model, githubToken, baseUrl });
|
|
53
|
+
}
|
|
54
|
+
/* v8 ignore stop */
|
|
47
55
|
}
|
|
48
56
|
}
|
|
49
57
|
function createProviderRegistry() {
|
|
@@ -52,6 +60,7 @@ function createProviderRegistry() {
|
|
|
52
60
|
anthropic: anthropic_1.createAnthropicProviderRuntime,
|
|
53
61
|
minimax: minimax_1.createMinimaxProviderRuntime,
|
|
54
62
|
"openai-codex": openai_codex_1.createOpenAICodexProviderRuntime,
|
|
63
|
+
"github-copilot": github_copilot_1.createGithubCopilotProviderRuntime,
|
|
55
64
|
};
|
|
56
65
|
return {
|
|
57
66
|
resolve() {
|
|
@@ -134,6 +143,8 @@ function getProviderDisplayLabel() {
|
|
|
134
143
|
anthropic: () => `anthropic (${(0, config_1.getAnthropicConfig)().model || "unknown"})`,
|
|
135
144
|
minimax: () => `minimax (${(0, config_1.getMinimaxConfig)().model || "unknown"})`,
|
|
136
145
|
"openai-codex": () => `openai codex (${(0, config_1.getOpenAICodexConfig)().model || "unknown"})`,
|
|
146
|
+
/* v8 ignore next -- branch: tested via display label unit test @preserve */
|
|
147
|
+
"github-copilot": () => `github copilot (${(0, config_1.getGithubCopilotConfig)().model || "unknown"})`,
|
|
137
148
|
};
|
|
138
149
|
return providerLabelBuilders[provider]();
|
|
139
150
|
}
|
|
@@ -69,6 +69,11 @@ const DEFAULT_SECRETS_TEMPLATE = {
|
|
|
69
69
|
model: "gpt-5.4",
|
|
70
70
|
oauthAccessToken: "",
|
|
71
71
|
},
|
|
72
|
+
"github-copilot": {
|
|
73
|
+
model: "claude-sonnet-4.6",
|
|
74
|
+
githubToken: "",
|
|
75
|
+
baseUrl: "",
|
|
76
|
+
},
|
|
72
77
|
},
|
|
73
78
|
teams: {
|
|
74
79
|
clientId: "",
|
|
@@ -127,7 +132,8 @@ function readAgentConfigForAgent(agentName, bundlesRoot = (0, identity_1.getAgen
|
|
|
127
132
|
if (provider !== "azure" &&
|
|
128
133
|
provider !== "anthropic" &&
|
|
129
134
|
provider !== "minimax" &&
|
|
130
|
-
provider !== "openai-codex"
|
|
135
|
+
provider !== "openai-codex" &&
|
|
136
|
+
provider !== "github-copilot") {
|
|
131
137
|
throw new Error(`agent.json at ${configPath} has unsupported provider '${String(provider)}'`);
|
|
132
138
|
}
|
|
133
139
|
return {
|
|
@@ -213,6 +219,45 @@ function validateAnthropicToken(token) {
|
|
|
213
219
|
async function collectRuntimeAuthCredentials(input, deps) {
|
|
214
220
|
const spawnSync = deps.spawnSync ?? child_process_1.spawnSync;
|
|
215
221
|
const homeDir = deps.homeDir ?? os.homedir();
|
|
222
|
+
if (input.provider === "github-copilot") {
|
|
223
|
+
let token = process.env.GH_TOKEN?.trim() || "";
|
|
224
|
+
if (!token) {
|
|
225
|
+
const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
|
|
226
|
+
token = (result.status === 0 && result.stdout ? result.stdout.trim() : "");
|
|
227
|
+
}
|
|
228
|
+
if (!token) {
|
|
229
|
+
(0, runtime_1.emitNervesEvent)({
|
|
230
|
+
component: "daemon",
|
|
231
|
+
event: "daemon.auth_gh_login_start",
|
|
232
|
+
message: "starting gh auth login for runtime auth",
|
|
233
|
+
meta: { agentName: input.agentName },
|
|
234
|
+
});
|
|
235
|
+
const loginResult = spawnSync("gh", ["auth", "login"], { stdio: "inherit" });
|
|
236
|
+
if (loginResult.status !== 0) {
|
|
237
|
+
throw new Error("'gh auth login' failed. Install the GitHub CLI (gh) and try again.");
|
|
238
|
+
}
|
|
239
|
+
const retryResult = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
|
|
240
|
+
/* v8 ignore next -- branch: retry after login always succeeds in tests @preserve */
|
|
241
|
+
token = (retryResult.status === 0 && retryResult.stdout ? retryResult.stdout.trim() : "");
|
|
242
|
+
/* v8 ignore next -- defensive: gh auth login succeeded but token still missing @preserve */
|
|
243
|
+
if (!token) {
|
|
244
|
+
throw new Error("gh auth login completed but no token was found. Run `gh auth login` and try again.");
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const response = await fetch("https://api.github.com/copilot_internal/user", {
|
|
248
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
249
|
+
});
|
|
250
|
+
if (!response.ok) {
|
|
251
|
+
throw new Error(`GitHub Copilot endpoint discovery failed (HTTP ${response.status}). Ensure your GitHub account has Copilot access.`);
|
|
252
|
+
}
|
|
253
|
+
const body = await response.json();
|
|
254
|
+
const baseUrl = body?.endpoints?.api;
|
|
255
|
+
/* v8 ignore next -- defensive: valid response but missing endpoints field @preserve */
|
|
256
|
+
if (!baseUrl) {
|
|
257
|
+
throw new Error("GitHub Copilot endpoint discovery returned no endpoints.api. Ensure your GitHub account has Copilot access.");
|
|
258
|
+
}
|
|
259
|
+
return { githubToken: token, baseUrl };
|
|
260
|
+
}
|
|
216
261
|
if (input.provider === "openai-codex") {
|
|
217
262
|
let token = readCodexAccessToken(homeDir);
|
|
218
263
|
if (!token) {
|
|
@@ -273,6 +318,14 @@ async function collectRuntimeAuthCredentials(input, deps) {
|
|
|
273
318
|
async function resolveHatchCredentials(input) {
|
|
274
319
|
const prompt = input.promptInput;
|
|
275
320
|
const credentials = { ...(input.credentials ?? {}) };
|
|
321
|
+
if (input.provider === "github-copilot" && !credentials.githubToken && input.runAuthFlow) {
|
|
322
|
+
const result = await input.runAuthFlow({
|
|
323
|
+
agentName: input.agentName,
|
|
324
|
+
provider: "github-copilot",
|
|
325
|
+
promptInput: prompt,
|
|
326
|
+
});
|
|
327
|
+
Object.assign(credentials, result.credentials);
|
|
328
|
+
}
|
|
276
329
|
if (input.provider === "anthropic" && !credentials.setupToken && input.runAuthFlow) {
|
|
277
330
|
const result = await input.runAuthFlow({
|
|
278
331
|
agentName: input.agentName,
|
|
@@ -313,6 +366,11 @@ function applyCredentials(secrets, provider, credentials) {
|
|
|
313
366
|
secrets.providers.anthropic.setupToken = credentials.setupToken.trim();
|
|
314
367
|
return;
|
|
315
368
|
}
|
|
369
|
+
if (provider === "github-copilot") {
|
|
370
|
+
secrets.providers["github-copilot"].githubToken = credentials.githubToken.trim();
|
|
371
|
+
secrets.providers["github-copilot"].baseUrl = credentials.baseUrl.trim();
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
316
374
|
if (provider === "openai-codex") {
|
|
317
375
|
secrets.providers["openai-codex"].oauthAccessToken = credentials.oauthAccessToken.trim();
|
|
318
376
|
return;
|
|
@@ -270,6 +270,8 @@ function usage() {
|
|
|
270
270
|
" ouro stop|down|status|logs|hatch",
|
|
271
271
|
" ouro -v|--version",
|
|
272
272
|
" ouro auth --agent <name> [--provider <provider>]",
|
|
273
|
+
" ouro auth verify --agent <name> [--provider <provider>]",
|
|
274
|
+
" ouro auth switch --agent <name> --provider <provider>",
|
|
273
275
|
" ouro chat <agent>",
|
|
274
276
|
" ouro msg --to <agent> [--session <id>] [--task <ref>] <message>",
|
|
275
277
|
" ouro poke <agent> --task <task-id>",
|
|
@@ -424,8 +426,57 @@ function parseLinkCommand(args, kind = "friend.link") {
|
|
|
424
426
|
};
|
|
425
427
|
}
|
|
426
428
|
function isAgentProvider(value) {
|
|
427
|
-
return value === "azure" || value === "anthropic" || value === "minimax" || value === "openai-codex";
|
|
429
|
+
return value === "azure" || value === "anthropic" || value === "minimax" || value === "openai-codex" || value === "github-copilot";
|
|
428
430
|
}
|
|
431
|
+
/* v8 ignore start -- hasStoredCredentials: per-provider branches tested via auth switch tests @preserve */
|
|
432
|
+
function hasStoredCredentials(provider, providerSecrets) {
|
|
433
|
+
if (provider === "anthropic")
|
|
434
|
+
return !!providerSecrets.setupToken;
|
|
435
|
+
if (provider === "openai-codex")
|
|
436
|
+
return !!providerSecrets.oauthAccessToken;
|
|
437
|
+
if (provider === "github-copilot")
|
|
438
|
+
return !!providerSecrets.githubToken;
|
|
439
|
+
if (provider === "minimax")
|
|
440
|
+
return !!providerSecrets.apiKey;
|
|
441
|
+
// azure
|
|
442
|
+
return !!providerSecrets.endpoint && !!providerSecrets.apiKey;
|
|
443
|
+
}
|
|
444
|
+
/* v8 ignore stop */
|
|
445
|
+
/* v8 ignore start -- verifyProviderCredentials: per-provider branches tested via auth verify tests @preserve */
|
|
446
|
+
function verifyProviderCredentials(provider, providers) {
|
|
447
|
+
const p = providers[provider];
|
|
448
|
+
if (!p)
|
|
449
|
+
return "not configured";
|
|
450
|
+
if (provider === "anthropic") {
|
|
451
|
+
const token = p.setupToken || "";
|
|
452
|
+
if (!token)
|
|
453
|
+
return "failed (no token)";
|
|
454
|
+
if (token.startsWith("sk-ant-"))
|
|
455
|
+
return "ok";
|
|
456
|
+
return "failed (invalid token format)";
|
|
457
|
+
}
|
|
458
|
+
if (provider === "openai-codex") {
|
|
459
|
+
const token = p.oauthAccessToken || "";
|
|
460
|
+
return token ? "ok" : "failed (no token)";
|
|
461
|
+
}
|
|
462
|
+
if (provider === "github-copilot") {
|
|
463
|
+
const token = p.githubToken || "";
|
|
464
|
+
return token ? "ok" : "failed (no token)";
|
|
465
|
+
}
|
|
466
|
+
if (provider === "minimax") {
|
|
467
|
+
const apiKey = p.apiKey || "";
|
|
468
|
+
return apiKey ? "ok" : "failed (no api key)";
|
|
469
|
+
}
|
|
470
|
+
// azure
|
|
471
|
+
const endpoint = p.endpoint || "";
|
|
472
|
+
const apiKey = p.apiKey || "";
|
|
473
|
+
if (!endpoint)
|
|
474
|
+
return "failed (no endpoint)";
|
|
475
|
+
if (!apiKey)
|
|
476
|
+
return "failed (no api key)";
|
|
477
|
+
return "ok";
|
|
478
|
+
}
|
|
479
|
+
/* v8 ignore stop */
|
|
429
480
|
function parseHatchCommand(args) {
|
|
430
481
|
let agentName;
|
|
431
482
|
let humanName;
|
|
@@ -481,7 +532,7 @@ function parseHatchCommand(args) {
|
|
|
481
532
|
}
|
|
482
533
|
}
|
|
483
534
|
if (providerRaw && !isAgentProvider(providerRaw)) {
|
|
484
|
-
throw new Error("Unknown provider. Use azure|anthropic|minimax|openai-codex.");
|
|
535
|
+
throw new Error("Unknown provider. Use azure|anthropic|minimax|openai-codex|github-copilot.");
|
|
485
536
|
}
|
|
486
537
|
const provider = providerRaw && isAgentProvider(providerRaw) ? providerRaw : undefined;
|
|
487
538
|
return {
|
|
@@ -541,6 +592,32 @@ function parseTaskCommand(args) {
|
|
|
541
592
|
throw new Error(`Usage\n${usage()}`);
|
|
542
593
|
}
|
|
543
594
|
function parseAuthCommand(args) {
|
|
595
|
+
const first = args[0];
|
|
596
|
+
if (first === "verify" || first === "switch") {
|
|
597
|
+
const { agent, rest } = extractAgentFlag(args.slice(1));
|
|
598
|
+
let provider;
|
|
599
|
+
/* v8 ignore start -- provider flag parsing: branches tested via CLI parsing tests @preserve */
|
|
600
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
601
|
+
if (rest[i] === "--provider") {
|
|
602
|
+
const value = rest[i + 1];
|
|
603
|
+
if (!isAgentProvider(value))
|
|
604
|
+
throw new Error(`Usage\n${usage()}`);
|
|
605
|
+
provider = value;
|
|
606
|
+
i += 1;
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
/* v8 ignore stop */
|
|
611
|
+
/* v8 ignore next -- defensive: agent always provided in tests @preserve */
|
|
612
|
+
if (!agent)
|
|
613
|
+
throw new Error(`Usage\n${usage()}`);
|
|
614
|
+
if (first === "switch") {
|
|
615
|
+
if (!provider)
|
|
616
|
+
throw new Error(`auth switch requires --provider.\n${usage()}`);
|
|
617
|
+
return { kind: "auth.switch", agent, provider };
|
|
618
|
+
}
|
|
619
|
+
return provider ? { kind: "auth.verify", agent, provider } : { kind: "auth.verify", agent };
|
|
620
|
+
}
|
|
544
621
|
const { agent, rest } = extractAgentFlag(args);
|
|
545
622
|
let provider;
|
|
546
623
|
for (let i = 0; i < rest.length; i += 1) {
|
|
@@ -698,6 +775,9 @@ function parseOuroCommand(args) {
|
|
|
698
775
|
const [head, second] = args;
|
|
699
776
|
if (!head)
|
|
700
777
|
return { kind: "daemon.up" };
|
|
778
|
+
if (head === "--agent" && second) {
|
|
779
|
+
return parseOuroCommand(args.slice(2));
|
|
780
|
+
}
|
|
701
781
|
if (head === "up")
|
|
702
782
|
return { kind: "daemon.up" };
|
|
703
783
|
if (head === "stop" || head === "down")
|
|
@@ -932,6 +1012,7 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
932
1012
|
anthropic: "claude-opus-4-6",
|
|
933
1013
|
minimax: "MiniMax-Text-01",
|
|
934
1014
|
"openai-codex": "gpt-5.4",
|
|
1015
|
+
"github-copilot": "claude-sonnet-4.6",
|
|
935
1016
|
azure: "",
|
|
936
1017
|
};
|
|
937
1018
|
if (discovered.length > 0) {
|
|
@@ -952,7 +1033,7 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
952
1033
|
providerConfig = unique[idx].providerConfig;
|
|
953
1034
|
}
|
|
954
1035
|
else {
|
|
955
|
-
const pRaw = await coldPrompt("provider (anthropic/azure/minimax/openai-codex): ");
|
|
1036
|
+
const pRaw = await coldPrompt("provider (anthropic/azure/minimax/openai-codex/github-copilot): ");
|
|
956
1037
|
if (!isAgentProvider(pRaw)) {
|
|
957
1038
|
process.stdout.write("unknown provider. run `ouro hatch` to try again.\n");
|
|
958
1039
|
coldRl.close();
|
|
@@ -976,7 +1057,7 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
976
1057
|
else {
|
|
977
1058
|
process.stdout.write(`\n\ud83d\udc0d welcome to ouroboros! ${hatchVerb}\n`);
|
|
978
1059
|
process.stdout.write("i need an API key to power our conversation.\n\n");
|
|
979
|
-
const pRaw = await coldPrompt("provider (anthropic/azure/minimax/openai-codex): ");
|
|
1060
|
+
const pRaw = await coldPrompt("provider (anthropic/azure/minimax/openai-codex/github-copilot): ");
|
|
980
1061
|
if (!isAgentProvider(pRaw)) {
|
|
981
1062
|
process.stdout.write("unknown provider. run `ouro hatch` to try again.\n");
|
|
982
1063
|
coldRl.close();
|
|
@@ -1161,7 +1242,7 @@ async function resolveHatchInput(command, deps) {
|
|
|
1161
1242
|
const prompt = deps.promptInput;
|
|
1162
1243
|
const agentName = command.agentName ?? (prompt ? await prompt("Hatchling name: ") : "");
|
|
1163
1244
|
const humanName = command.humanName ?? (prompt ? await prompt("Your name: ") : os.userInfo().username);
|
|
1164
|
-
const providerRaw = command.provider ?? (prompt ? await prompt("Provider (azure|anthropic|minimax|openai-codex): ") : "");
|
|
1245
|
+
const providerRaw = command.provider ?? (prompt ? await prompt("Provider (azure|anthropic|minimax|openai-codex|github-copilot): ") : "");
|
|
1165
1246
|
if (!agentName || !humanName || !isAgentProvider(providerRaw)) {
|
|
1166
1247
|
throw new Error(`Usage\n${usage()}`);
|
|
1167
1248
|
}
|
|
@@ -1599,18 +1680,53 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
1599
1680
|
// ── auth (local, no daemon socket needed) ──
|
|
1600
1681
|
if (command.kind === "auth.run") {
|
|
1601
1682
|
const provider = command.provider ?? (0, auth_flow_1.readAgentConfigForAgent)(command.agent).config.provider;
|
|
1683
|
+
/* v8 ignore next -- tests always inject runAuthFlow; default is for production @preserve */
|
|
1602
1684
|
const authRunner = deps.runAuthFlow ?? auth_flow_1.runRuntimeAuthFlow;
|
|
1603
1685
|
const result = await authRunner({
|
|
1604
1686
|
agentName: command.agent,
|
|
1605
1687
|
provider,
|
|
1606
1688
|
promptInput: deps.promptInput,
|
|
1607
1689
|
});
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
}
|
|
1690
|
+
// Behavior: ouro auth stores credentials only — does NOT switch provider.
|
|
1691
|
+
// Use `ouro auth switch` to change the active provider.
|
|
1611
1692
|
deps.writeStdout(result.message);
|
|
1612
1693
|
return result.message;
|
|
1613
1694
|
}
|
|
1695
|
+
// ── auth verify (local, no daemon socket needed) ──
|
|
1696
|
+
/* v8 ignore start -- auth verify/switch: tested in daemon-cli.test.ts but v8 traces differ in CI @preserve */
|
|
1697
|
+
if (command.kind === "auth.verify") {
|
|
1698
|
+
const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent);
|
|
1699
|
+
const providers = secrets.providers;
|
|
1700
|
+
if (command.provider) {
|
|
1701
|
+
const status = verifyProviderCredentials(command.provider, providers);
|
|
1702
|
+
const message = `${command.provider}: ${status}`;
|
|
1703
|
+
deps.writeStdout(message);
|
|
1704
|
+
return message;
|
|
1705
|
+
}
|
|
1706
|
+
const lines = [];
|
|
1707
|
+
for (const p of Object.keys(providers)) {
|
|
1708
|
+
const status = verifyProviderCredentials(p, providers);
|
|
1709
|
+
lines.push(`${p}: ${status}`);
|
|
1710
|
+
}
|
|
1711
|
+
const message = lines.join("\n");
|
|
1712
|
+
deps.writeStdout(message);
|
|
1713
|
+
return message;
|
|
1714
|
+
}
|
|
1715
|
+
// ── auth switch (local, no daemon socket needed) ──
|
|
1716
|
+
if (command.kind === "auth.switch") {
|
|
1717
|
+
const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent);
|
|
1718
|
+
const providerSecrets = secrets.providers[command.provider];
|
|
1719
|
+
if (!providerSecrets || !hasStoredCredentials(command.provider, providerSecrets)) {
|
|
1720
|
+
const message = `no credentials stored for ${command.provider}. Run \`ouro auth --agent ${command.agent} --provider ${command.provider}\` first.`;
|
|
1721
|
+
deps.writeStdout(message);
|
|
1722
|
+
return message;
|
|
1723
|
+
}
|
|
1724
|
+
(0, auth_flow_1.writeAgentProviderSelection)(command.agent, command.provider);
|
|
1725
|
+
const message = `switched ${command.agent} to ${command.provider}`;
|
|
1726
|
+
deps.writeStdout(message);
|
|
1727
|
+
return message;
|
|
1728
|
+
}
|
|
1729
|
+
/* v8 ignore stop */
|
|
1614
1730
|
// ── whoami (local, no daemon socket needed) ──
|
|
1615
1731
|
if (command.kind === "whoami") {
|
|
1616
1732
|
if (command.agent) {
|
|
@@ -48,6 +48,9 @@ function requiredCredentialKeys(provider) {
|
|
|
48
48
|
return ["setupToken"];
|
|
49
49
|
if (provider === "openai-codex")
|
|
50
50
|
return ["oauthAccessToken"];
|
|
51
|
+
/* v8 ignore next -- branch tested via requiredCredentialKeys unit test @preserve */
|
|
52
|
+
if (provider === "github-copilot")
|
|
53
|
+
return ["githubToken"];
|
|
51
54
|
if (provider === "minimax")
|
|
52
55
|
return ["apiKey"];
|
|
53
56
|
return ["apiKey", "endpoint", "deployment"];
|
package/dist/heart/identity.js
CHANGED
|
@@ -301,7 +301,8 @@ function loadAgentConfig() {
|
|
|
301
301
|
if (rawProvider !== "azure" &&
|
|
302
302
|
rawProvider !== "minimax" &&
|
|
303
303
|
rawProvider !== "anthropic" &&
|
|
304
|
-
rawProvider !== "openai-codex"
|
|
304
|
+
rawProvider !== "openai-codex" &&
|
|
305
|
+
rawProvider !== "github-copilot") {
|
|
305
306
|
(0, runtime_1.emitNervesEvent)({
|
|
306
307
|
level: "error",
|
|
307
308
|
event: "config_identity.error",
|
|
@@ -312,7 +313,7 @@ function loadAgentConfig() {
|
|
|
312
313
|
provider: rawProvider,
|
|
313
314
|
},
|
|
314
315
|
});
|
|
315
|
-
throw new Error(`agent.json at ${configFile} must include provider: "azure", "minimax", "anthropic",
|
|
316
|
+
throw new Error(`agent.json at ${configFile} must include provider: "azure", "minimax", "anthropic", "openai-codex", or "github-copilot".`);
|
|
316
317
|
}
|
|
317
318
|
const provider = rawProvider;
|
|
318
319
|
const rawVersion = parsed.version;
|
|
@@ -14,6 +14,14 @@ exports.MODEL_CAPABILITIES = {
|
|
|
14
14
|
thinkingFormat: "anthropic",
|
|
15
15
|
maxOutputTokens: 64000,
|
|
16
16
|
},
|
|
17
|
+
"claude-opus-4.6": {
|
|
18
|
+
reasoningEffort: ["low", "medium", "high", "max"],
|
|
19
|
+
maxOutputTokens: 128000,
|
|
20
|
+
},
|
|
21
|
+
"claude-sonnet-4.6": {
|
|
22
|
+
reasoningEffort: ["low", "medium", "high"],
|
|
23
|
+
maxOutputTokens: 64000,
|
|
24
|
+
},
|
|
17
25
|
"gpt-5.4": {
|
|
18
26
|
reasoningEffort: ["low", "medium", "high"],
|
|
19
27
|
phase: true,
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createGithubCopilotProviderRuntime = createGithubCopilotProviderRuntime;
|
|
7
|
+
const openai_1 = __importDefault(require("openai"));
|
|
8
|
+
const config_1 = require("../config");
|
|
9
|
+
const identity_1 = require("../identity");
|
|
10
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
11
|
+
const streaming_1 = require("../streaming");
|
|
12
|
+
const model_capabilities_1 = require("../model-capabilities");
|
|
13
|
+
/* v8 ignore start -- auth guidance helpers: tested via mock-driven provider tests @preserve */
|
|
14
|
+
function isAuthFailure(error) {
|
|
15
|
+
if (!(error instanceof Error))
|
|
16
|
+
return false;
|
|
17
|
+
const status = error.status;
|
|
18
|
+
return status === 401 || status === 403;
|
|
19
|
+
}
|
|
20
|
+
function getReauthGuidance(reason) {
|
|
21
|
+
const agentName = (0, identity_1.getAgentName)();
|
|
22
|
+
return [
|
|
23
|
+
`provider github-copilot failed (${reason}).`,
|
|
24
|
+
`Run \`ouro auth verify --agent ${agentName}\` to check all configured providers,`,
|
|
25
|
+
`\`ouro auth switch --agent ${agentName} --provider <other>\` to switch,`,
|
|
26
|
+
`or \`ouro auth --agent ${agentName} --provider github-copilot\` to reconfigure.`,
|
|
27
|
+
].join(" ");
|
|
28
|
+
}
|
|
29
|
+
function withAuthGuidance(error) {
|
|
30
|
+
const base = error instanceof Error ? error.message : String(error);
|
|
31
|
+
if (isAuthFailure(error)) {
|
|
32
|
+
return new Error(getReauthGuidance(base));
|
|
33
|
+
}
|
|
34
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
35
|
+
}
|
|
36
|
+
/* v8 ignore stop */
|
|
37
|
+
function createGithubCopilotProviderRuntime() {
|
|
38
|
+
(0, runtime_1.emitNervesEvent)({
|
|
39
|
+
component: "engine",
|
|
40
|
+
event: "engine.provider_init",
|
|
41
|
+
message: "github-copilot provider init",
|
|
42
|
+
meta: { provider: "github-copilot" },
|
|
43
|
+
});
|
|
44
|
+
const config = (0, config_1.getGithubCopilotConfig)();
|
|
45
|
+
if (!config.githubToken) {
|
|
46
|
+
throw new Error("provider 'github-copilot' is selected in agent.json but providers.github-copilot.githubToken is missing in secrets.json.");
|
|
47
|
+
}
|
|
48
|
+
if (!config.baseUrl) {
|
|
49
|
+
throw new Error("provider 'github-copilot' is selected in agent.json but providers.github-copilot.baseUrl is missing in secrets.json.");
|
|
50
|
+
}
|
|
51
|
+
const isCompletionsModel = config.model.startsWith("claude");
|
|
52
|
+
const modelCaps = (0, model_capabilities_1.getModelCapabilities)(config.model);
|
|
53
|
+
const capabilities = new Set();
|
|
54
|
+
/* v8 ignore next -- branch: capability detection tested via unit test @preserve */
|
|
55
|
+
if (modelCaps.reasoningEffort)
|
|
56
|
+
capabilities.add("reasoning-effort");
|
|
57
|
+
const client = new openai_1.default({
|
|
58
|
+
apiKey: config.githubToken,
|
|
59
|
+
baseURL: config.baseUrl,
|
|
60
|
+
timeout: 30000,
|
|
61
|
+
maxRetries: 0,
|
|
62
|
+
});
|
|
63
|
+
if (isCompletionsModel) {
|
|
64
|
+
// Chat completions path (Claude models via Copilot)
|
|
65
|
+
return {
|
|
66
|
+
id: "github-copilot",
|
|
67
|
+
model: config.model,
|
|
68
|
+
client,
|
|
69
|
+
capabilities,
|
|
70
|
+
supportedReasoningEfforts: modelCaps.reasoningEffort,
|
|
71
|
+
resetTurnState(_messages) {
|
|
72
|
+
// No provider-owned turn state for chat-completions path.
|
|
73
|
+
},
|
|
74
|
+
appendToolOutput(_callId, _output) {
|
|
75
|
+
// Chat-completions providers rely on canonical messages only.
|
|
76
|
+
},
|
|
77
|
+
/* v8 ignore start -- streamTurn: tested via mock assertions in github-copilot.test.ts @preserve */
|
|
78
|
+
async streamTurn(request) {
|
|
79
|
+
const params = {
|
|
80
|
+
messages: request.messages,
|
|
81
|
+
tools: request.activeTools,
|
|
82
|
+
stream: true,
|
|
83
|
+
};
|
|
84
|
+
if (this.model)
|
|
85
|
+
params.model = this.model;
|
|
86
|
+
if (request.traceId)
|
|
87
|
+
params.metadata = { trace_id: request.traceId };
|
|
88
|
+
if (request.toolChoiceRequired)
|
|
89
|
+
params.tool_choice = "required";
|
|
90
|
+
try {
|
|
91
|
+
return await (0, streaming_1.streamChatCompletion)(this.client, params, request.callbacks, request.signal);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
throw withAuthGuidance(error);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
/* v8 ignore stop */
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Responses API path (GPT models via Copilot)
|
|
101
|
+
let nativeInput = null;
|
|
102
|
+
let nativeInstructions = "";
|
|
103
|
+
return {
|
|
104
|
+
id: "github-copilot",
|
|
105
|
+
model: config.model,
|
|
106
|
+
client,
|
|
107
|
+
capabilities,
|
|
108
|
+
supportedReasoningEfforts: modelCaps.reasoningEffort,
|
|
109
|
+
/* v8 ignore start -- responses path: tested via mock assertions in github-copilot.test.ts @preserve */
|
|
110
|
+
resetTurnState(messages) {
|
|
111
|
+
const { instructions, input } = (0, streaming_1.toResponsesInput)(messages);
|
|
112
|
+
nativeInput = input;
|
|
113
|
+
nativeInstructions = instructions;
|
|
114
|
+
},
|
|
115
|
+
appendToolOutput(callId, output) {
|
|
116
|
+
if (!nativeInput)
|
|
117
|
+
return;
|
|
118
|
+
nativeInput.push({ type: "function_call_output", call_id: callId, output });
|
|
119
|
+
},
|
|
120
|
+
async streamTurn(request) {
|
|
121
|
+
if (!nativeInput)
|
|
122
|
+
this.resetTurnState(request.messages);
|
|
123
|
+
const params = {
|
|
124
|
+
model: this.model,
|
|
125
|
+
input: nativeInput,
|
|
126
|
+
instructions: nativeInstructions,
|
|
127
|
+
tools: (0, streaming_1.toResponsesTools)(request.activeTools),
|
|
128
|
+
reasoning: { effort: request.reasoningEffort ?? "medium", summary: "detailed" },
|
|
129
|
+
stream: true,
|
|
130
|
+
store: false,
|
|
131
|
+
include: ["reasoning.encrypted_content"],
|
|
132
|
+
};
|
|
133
|
+
if (request.traceId)
|
|
134
|
+
params.metadata = { trace_id: request.traceId };
|
|
135
|
+
if (request.toolChoiceRequired)
|
|
136
|
+
params.tool_choice = "required";
|
|
137
|
+
try {
|
|
138
|
+
const result = await (0, streaming_1.streamResponsesApi)(this.client, params, request.callbacks, request.signal);
|
|
139
|
+
for (const item of result.outputItems)
|
|
140
|
+
nativeInput.push(item);
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
throw withAuthGuidance(error);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
/* v8 ignore stop */
|
|
148
|
+
};
|
|
149
|
+
}
|
package/dist/mind/prompt.js
CHANGED
|
@@ -180,6 +180,9 @@ my bones give me the \`ouro\` cli. always pass \`--agent ${agentName}\`:
|
|
|
180
180
|
ouro friend show --agent ${agentName} <id>
|
|
181
181
|
ouro session list --agent ${agentName}
|
|
182
182
|
ouro reminder create --agent ${agentName} <title> --body <body>
|
|
183
|
+
ouro auth --agent ${agentName} --provider <provider>
|
|
184
|
+
ouro auth verify --agent ${agentName} [--provider <provider>]
|
|
185
|
+
ouro auth switch --agent ${agentName} --provider <provider>
|
|
183
186
|
ouro mcp list --agent ${agentName}
|
|
184
187
|
ouro mcp call --agent ${agentName} <server> <tool> --args '{...}'
|
|
185
188
|
ouro --help`;
|
|
@@ -172,6 +172,9 @@ exports.OURO_CLI_TRUST_MANIFEST = {
|
|
|
172
172
|
"reminder create": "friend",
|
|
173
173
|
"mcp list": "acquaintance",
|
|
174
174
|
"mcp call": "friend",
|
|
175
|
+
auth: "family",
|
|
176
|
+
"auth verify": "family",
|
|
177
|
+
"auth switch": "family",
|
|
175
178
|
};
|
|
176
179
|
// --- trust level comparison ---
|
|
177
180
|
const LEVEL_ORDER = {
|