@ouro.bot/cli 0.1.0-alpha.7 → 0.1.0-alpha.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/AdoptionSpecialist.ouro/agent.json +70 -9
  2. package/AdoptionSpecialist.ouro/psyche/SOUL.md +5 -2
  3. package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
  4. package/README.md +147 -205
  5. package/assets/ouroboros.png +0 -0
  6. package/changelog.json +395 -0
  7. package/dist/heart/active-work.js +178 -0
  8. package/dist/heart/bridges/manager.js +358 -0
  9. package/dist/heart/bridges/state-machine.js +135 -0
  10. package/dist/heart/bridges/store.js +123 -0
  11. package/dist/heart/config.js +68 -23
  12. package/dist/heart/core.js +282 -92
  13. package/dist/heart/cross-chat-delivery.js +146 -0
  14. package/dist/heart/daemon/agent-discovery.js +81 -0
  15. package/dist/heart/daemon/auth-flow.js +409 -0
  16. package/dist/heart/daemon/daemon-cli.js +1408 -248
  17. package/dist/heart/daemon/daemon-entry.js +55 -6
  18. package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
  19. package/dist/heart/daemon/daemon.js +216 -10
  20. package/dist/heart/daemon/hatch-animation.js +10 -3
  21. package/dist/heart/daemon/hatch-flow.js +7 -82
  22. package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
  23. package/dist/heart/daemon/launchd.js +159 -0
  24. package/dist/heart/daemon/log-tailer.js +4 -3
  25. package/dist/heart/daemon/message-router.js +17 -8
  26. package/dist/heart/daemon/ouro-bot-entry.js +0 -0
  27. package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
  28. package/dist/heart/daemon/ouro-entry.js +0 -0
  29. package/dist/heart/daemon/ouro-path-installer.js +178 -0
  30. package/dist/heart/daemon/ouro-uti.js +11 -2
  31. package/dist/heart/daemon/process-manager.js +14 -1
  32. package/dist/heart/daemon/run-hooks.js +37 -0
  33. package/dist/heart/daemon/runtime-logging.js +58 -15
  34. package/dist/heart/daemon/runtime-metadata.js +219 -0
  35. package/dist/heart/daemon/runtime-mode.js +67 -0
  36. package/dist/heart/daemon/sense-manager.js +307 -0
  37. package/dist/heart/daemon/skill-management-installer.js +94 -0
  38. package/dist/heart/daemon/socket-client.js +202 -0
  39. package/dist/heart/daemon/specialist-orchestrator.js +53 -84
  40. package/dist/heart/daemon/specialist-prompt.js +64 -5
  41. package/dist/heart/daemon/specialist-tools.js +213 -58
  42. package/dist/heart/daemon/staged-restart.js +114 -0
  43. package/dist/heart/daemon/thoughts.js +379 -0
  44. package/dist/heart/daemon/update-checker.js +111 -0
  45. package/dist/heart/daemon/update-hooks.js +138 -0
  46. package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
  47. package/dist/heart/delegation.js +62 -0
  48. package/dist/heart/identity.js +126 -21
  49. package/dist/heart/kicks.js +1 -19
  50. package/dist/heart/model-capabilities.js +48 -0
  51. package/dist/heart/progress-story.js +42 -0
  52. package/dist/heart/providers/anthropic.js +74 -9
  53. package/dist/heart/providers/azure.js +86 -7
  54. package/dist/heart/providers/github-copilot.js +149 -0
  55. package/dist/heart/providers/minimax.js +4 -0
  56. package/dist/heart/providers/openai-codex.js +12 -3
  57. package/dist/heart/safe-workspace.js +228 -0
  58. package/dist/heart/sense-truth.js +61 -0
  59. package/dist/heart/session-activity.js +169 -0
  60. package/dist/heart/session-recall.js +116 -0
  61. package/dist/heart/streaming.js +100 -22
  62. package/dist/heart/target-resolution.js +123 -0
  63. package/dist/heart/turn-coordinator.js +28 -0
  64. package/dist/mind/associative-recall.js +14 -2
  65. package/dist/mind/bundle-manifest.js +70 -0
  66. package/dist/mind/context.js +27 -11
  67. package/dist/mind/first-impressions.js +16 -2
  68. package/dist/mind/friends/channel.js +35 -0
  69. package/dist/mind/friends/group-context.js +144 -0
  70. package/dist/mind/friends/store-file.js +19 -0
  71. package/dist/mind/friends/trust-explanation.js +74 -0
  72. package/dist/mind/friends/types.js +8 -0
  73. package/dist/mind/memory.js +27 -26
  74. package/dist/mind/pending.js +72 -9
  75. package/dist/mind/phrases.js +1 -0
  76. package/dist/mind/prompt.js +358 -77
  77. package/dist/mind/token-estimate.js +8 -12
  78. package/dist/nerves/cli-logging.js +15 -2
  79. package/dist/nerves/coverage/run-artifacts.js +1 -1
  80. package/dist/repertoire/ado-client.js +4 -2
  81. package/dist/repertoire/coding/feedback.js +134 -0
  82. package/dist/repertoire/coding/index.js +4 -1
  83. package/dist/repertoire/coding/manager.js +62 -4
  84. package/dist/repertoire/coding/spawner.js +3 -3
  85. package/dist/repertoire/coding/tools.js +41 -2
  86. package/dist/repertoire/data/ado-endpoints.json +188 -0
  87. package/dist/repertoire/guardrails.js +279 -0
  88. package/dist/repertoire/mcp-client.js +254 -0
  89. package/dist/repertoire/mcp-manager.js +195 -0
  90. package/dist/repertoire/skills.js +3 -26
  91. package/dist/repertoire/tasks/board.js +12 -0
  92. package/dist/repertoire/tasks/index.js +23 -9
  93. package/dist/repertoire/tasks/transitions.js +1 -2
  94. package/dist/repertoire/tools-base.js +642 -251
  95. package/dist/repertoire/tools-bluebubbles.js +93 -0
  96. package/dist/repertoire/tools-teams.js +58 -25
  97. package/dist/repertoire/tools.js +93 -52
  98. package/dist/senses/bluebubbles-client.js +210 -5
  99. package/dist/senses/bluebubbles-entry.js +2 -0
  100. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  101. package/dist/senses/bluebubbles-media.js +339 -0
  102. package/dist/senses/bluebubbles-model.js +12 -4
  103. package/dist/senses/bluebubbles-mutation-log.js +45 -5
  104. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  105. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  106. package/dist/senses/bluebubbles.js +893 -45
  107. package/dist/senses/cli-layout.js +87 -0
  108. package/dist/senses/cli.js +348 -144
  109. package/dist/senses/continuity.js +94 -0
  110. package/dist/senses/debug-activity.js +148 -0
  111. package/dist/senses/inner-dialog-worker.js +47 -18
  112. package/dist/senses/inner-dialog.js +333 -84
  113. package/dist/senses/pipeline.js +278 -0
  114. package/dist/senses/teams.js +573 -129
  115. package/dist/senses/trust-gate.js +112 -2
  116. package/package.json +14 -3
  117. package/subagents/README.md +4 -70
  118. package/dist/heart/daemon/specialist-session.js +0 -142
  119. package/dist/heart/daemon/subagent-installer.js +0 -125
  120. package/dist/inner-worker-entry.js +0 -4
  121. package/subagents/work-doer.md +0 -233
  122. package/subagents/work-merger.md +0 -624
  123. package/subagents/work-planner.md +0 -373
@@ -3,11 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.toAnthropicMessages = toAnthropicMessages;
6
7
  exports.createAnthropicProviderRuntime = createAnthropicProviderRuntime;
7
8
  const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
8
9
  const config_1 = require("../config");
9
10
  const identity_1 = require("../identity");
10
11
  const runtime_1 = require("../../nerves/runtime");
12
+ const streaming_1 = require("../streaming");
13
+ const model_capabilities_1 = require("../model-capabilities");
11
14
  const ANTHROPIC_SETUP_TOKEN_PREFIX = "sk-ant-oat01-";
12
15
  const ANTHROPIC_SETUP_TOKEN_MIN_LENGTH = 80;
13
16
  const ANTHROPIC_OAUTH_BETA_HEADER = "claude-code-20250219,oauth-2025-04-20,fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14";
@@ -21,10 +24,10 @@ function getAnthropicSetupTokenInstructions() {
21
24
  const agentName = getAnthropicAgentNameForGuidance();
22
25
  return [
23
26
  "Fix:",
24
- ` 1. Run \`npm run auth:claude-setup-token -- --agent ${agentName}\``,
25
- " (or run `claude setup-token` and paste the token manually)",
27
+ ` 1. Run \`ouro auth --agent ${agentName}\``,
26
28
  ` 2. Open ${getAnthropicSecretsPathForGuidance()}`,
27
29
  " 3. Confirm providers.anthropic.setupToken is set",
30
+ " 4. After reauth, retry the failed ouro command or reconnect this session.",
28
31
  ].join("\n");
29
32
  }
30
33
  function getAnthropicReauthGuidance(reason) {
@@ -92,6 +95,18 @@ function toAnthropicMessages(messages) {
92
95
  if (msg.role === "assistant") {
93
96
  const assistant = msg;
94
97
  const blocks = [];
98
+ // Restore thinking blocks before text/tool_use blocks
99
+ const thinkingBlocks = assistant._thinking_blocks;
100
+ if (thinkingBlocks) {
101
+ for (const tb of thinkingBlocks) {
102
+ if (tb.type === "thinking") {
103
+ blocks.push({ type: "thinking", thinking: tb.thinking, signature: tb.signature });
104
+ }
105
+ else {
106
+ blocks.push({ type: "redacted_thinking", data: tb.data });
107
+ }
108
+ }
109
+ }
95
110
  const text = toAnthropicTextContent(assistant.content);
96
111
  if (text) {
97
112
  blocks.push({ type: "text", text });
@@ -194,11 +209,14 @@ function withAnthropicAuthGuidance(error) {
194
209
  async function streamAnthropicMessages(client, model, request) {
195
210
  const { system, messages } = toAnthropicMessages(request.messages);
196
211
  const anthropicTools = toAnthropicTools(request.activeTools);
212
+ const modelCaps = (0, model_capabilities_1.getModelCapabilities)(model);
213
+ const maxTokens = modelCaps.maxOutputTokens ?? 16384;
197
214
  const params = {
198
215
  model,
199
- max_tokens: 4096,
216
+ max_tokens: maxTokens,
200
217
  messages,
201
218
  stream: true,
219
+ thinking: { type: "adaptive", effort: request.reasoningEffort ?? "medium" },
202
220
  };
203
221
  if (system)
204
222
  params.system = system;
@@ -218,6 +236,9 @@ async function streamAnthropicMessages(client, model, request) {
218
236
  let streamStarted = false;
219
237
  let usage;
220
238
  const toolCalls = new Map();
239
+ const thinkingBlocks = new Map();
240
+ const redactedBlocks = new Map();
241
+ const answerStreamer = new streaming_1.FinalAnswerStreamer(request.callbacks);
221
242
  try {
222
243
  for await (const event of response) {
223
244
  if (request.signal?.aborted)
@@ -225,17 +246,29 @@ async function streamAnthropicMessages(client, model, request) {
225
246
  const eventType = String(event.type ?? "");
226
247
  if (eventType === "content_block_start") {
227
248
  const block = event.content_block;
228
- if (block?.type === "tool_use") {
229
- const index = Number(event.index);
249
+ const index = Number(event.index);
250
+ if (block?.type === "thinking") {
251
+ thinkingBlocks.set(index, { type: "thinking", thinking: "", signature: "" });
252
+ }
253
+ else if (block?.type === "redacted_thinking") {
254
+ redactedBlocks.set(index, { type: "redacted_thinking", data: String(block.data ?? "") });
255
+ }
256
+ else if (block?.type === "tool_use") {
230
257
  const rawInput = block.input;
231
258
  const input = rawInput && typeof rawInput === "object"
232
259
  ? JSON.stringify(rawInput)
233
260
  : "";
261
+ const name = String(block.name ?? "");
234
262
  toolCalls.set(index, {
235
263
  id: String(block.id ?? ""),
236
- name: String(block.name ?? ""),
264
+ name,
237
265
  arguments: input,
238
266
  });
267
+ // Activate eager streaming for sole final_answer tool call
268
+ /* v8 ignore next -- final_answer streaming activation, tested via FinalAnswerStreamer unit tests @preserve */
269
+ if (name === "final_answer" && toolCalls.size === 1) {
270
+ answerStreamer.activate();
271
+ }
239
272
  }
240
273
  continue;
241
274
  }
@@ -257,14 +290,31 @@ async function streamAnthropicMessages(client, model, request) {
257
290
  request.callbacks.onModelStreamStart();
258
291
  streamStarted = true;
259
292
  }
260
- request.callbacks.onReasoningChunk(String(delta?.thinking ?? ""));
293
+ const thinkingText = String(delta?.thinking ?? "");
294
+ request.callbacks.onReasoningChunk(thinkingText);
295
+ const thinkingIndex = Number(event.index);
296
+ const thinkingBlock = thinkingBlocks.get(thinkingIndex);
297
+ if (thinkingBlock)
298
+ thinkingBlock.thinking += thinkingText;
299
+ continue;
300
+ }
301
+ if (deltaType === "signature_delta") {
302
+ const sigIndex = Number(event.index);
303
+ const sigBlock = thinkingBlocks.get(sigIndex);
304
+ if (sigBlock)
305
+ sigBlock.signature += String(delta?.signature ?? "");
261
306
  continue;
262
307
  }
263
308
  if (deltaType === "input_json_delta") {
264
309
  const index = Number(event.index);
265
310
  const existing = toolCalls.get(index);
266
311
  if (existing) {
267
- existing.arguments = mergeAnthropicToolArguments(existing.arguments, String(delta?.partial_json ?? ""));
312
+ const partialJson = String(delta?.partial_json ?? "");
313
+ existing.arguments = mergeAnthropicToolArguments(existing.arguments, partialJson);
314
+ /* v8 ignore next -- final_answer delta streaming, tested via FinalAnswerStreamer unit tests @preserve */
315
+ if (existing.name === "final_answer" && toolCalls.size === 1) {
316
+ answerStreamer.processDelta(partialJson);
317
+ }
268
318
  }
269
319
  continue;
270
320
  }
@@ -288,11 +338,20 @@ async function streamAnthropicMessages(client, model, request) {
288
338
  catch (error) {
289
339
  throw withAnthropicAuthGuidance(error);
290
340
  }
341
+ // Collect all thinking blocks (regular + redacted) sorted by index to preserve ordering
342
+ const allThinkingIndices = [...thinkingBlocks.keys(), ...redactedBlocks.keys()].sort((a, b) => a - b);
343
+ const outputItems = allThinkingIndices.map((idx) => {
344
+ const tb = thinkingBlocks.get(idx);
345
+ if (tb)
346
+ return tb;
347
+ return redactedBlocks.get(idx);
348
+ });
291
349
  return {
292
350
  content,
293
351
  toolCalls: [...toolCalls.values()],
294
- outputItems: [],
352
+ outputItems,
295
353
  usage,
354
+ finalAnswerStreamed: answerStreamer.streamed,
296
355
  };
297
356
  }
298
357
  function createAnthropicProviderRuntime() {
@@ -306,6 +365,10 @@ function createAnthropicProviderRuntime() {
306
365
  if (!(anthropicConfig.model && anthropicConfig.setupToken)) {
307
366
  throw new Error(getAnthropicReauthGuidance("provider 'anthropic' is selected in agent.json but providers.anthropic.model/setupToken is incomplete in secrets.json."));
308
367
  }
368
+ const modelCaps = (0, model_capabilities_1.getModelCapabilities)(anthropicConfig.model);
369
+ const capabilities = new Set();
370
+ if (modelCaps.reasoningEffort)
371
+ capabilities.add("reasoning-effort");
309
372
  const credential = resolveAnthropicSetupTokenCredential();
310
373
  const client = new sdk_1.default({
311
374
  authToken: credential.token,
@@ -319,6 +382,8 @@ function createAnthropicProviderRuntime() {
319
382
  id: "anthropic",
320
383
  model: anthropicConfig.model,
321
384
  client,
385
+ capabilities,
386
+ supportedReasoningEfforts: modelCaps.reasoningEffort,
322
387
  resetTurnState(_messages) {
323
388
  // Anthropic request payload is derived from canonical messages each turn.
324
389
  },
@@ -1,35 +1,114 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.createAzureTokenProvider = createAzureTokenProvider;
3
37
  exports.createAzureProviderRuntime = createAzureProviderRuntime;
4
38
  const openai_1 = require("openai");
5
39
  const config_1 = require("../config");
6
40
  const runtime_1 = require("../../nerves/runtime");
7
41
  const streaming_1 = require("../streaming");
42
+ const model_capabilities_1 = require("../model-capabilities");
43
+ const COGNITIVE_SERVICES_SCOPE = "https://cognitiveservices.azure.com/.default";
44
+ // @azure/identity is imported dynamically (below) rather than at the top level
45
+ // because it's a heavy package (~30+ transitive deps) and we only need it when
46
+ // using the managed-identity auth path. API-key users and other providers
47
+ // shouldn't pay the cold-start cost.
48
+ function createAzureTokenProvider(managedIdentityClientId) {
49
+ let credential = null;
50
+ return async () => {
51
+ try {
52
+ if (!credential) {
53
+ const { DefaultAzureCredential } = await Promise.resolve().then(() => __importStar(require("@azure/identity")));
54
+ const credentialOptions = managedIdentityClientId
55
+ ? { managedIdentityClientId }
56
+ : undefined;
57
+ credential = new DefaultAzureCredential(credentialOptions);
58
+ }
59
+ const tokenResponse = await credential.getToken(COGNITIVE_SERVICES_SCOPE);
60
+ return tokenResponse.token;
61
+ }
62
+ catch (err) {
63
+ const detail = err instanceof Error ? err.message : String(err);
64
+ throw new Error(`Azure OpenAI authentication failed: ${detail}\n` +
65
+ "To fix this, either:\n" +
66
+ " 1. Set providers.azure.apiKey in secrets.json, or\n" +
67
+ " 2. Run 'az login' to authenticate with your Azure account (for local dev), or\n" +
68
+ " 3. Attach a managed identity to your App Service and set providers.azure.managedIdentityClientId in secrets.json (for deployed environments)");
69
+ }
70
+ };
71
+ }
8
72
  function createAzureProviderRuntime() {
73
+ const azureConfig = (0, config_1.getAzureConfig)();
74
+ const useApiKey = !!azureConfig.apiKey;
75
+ const authMethod = useApiKey ? "key" : "managed-identity";
9
76
  (0, runtime_1.emitNervesEvent)({
10
77
  component: "engine",
11
78
  event: "engine.provider_init",
12
79
  message: "azure provider init",
13
- meta: { provider: "azure" },
80
+ meta: { provider: "azure", authMethod },
14
81
  });
15
- const azureConfig = (0, config_1.getAzureConfig)();
16
- if (!(azureConfig.apiKey && azureConfig.endpoint && azureConfig.deployment && azureConfig.modelName)) {
82
+ if (!(azureConfig.endpoint && azureConfig.deployment && azureConfig.modelName)) {
17
83
  throw new Error("provider 'azure' is selected in agent.json but providers.azure is incomplete in secrets.json.");
18
84
  }
19
- const client = new openai_1.AzureOpenAI({
20
- apiKey: azureConfig.apiKey,
85
+ const modelCaps = (0, model_capabilities_1.getModelCapabilities)(azureConfig.modelName);
86
+ const capabilities = new Set();
87
+ if (modelCaps.reasoningEffort)
88
+ capabilities.add("reasoning-effort");
89
+ const clientOptions = {
21
90
  endpoint: azureConfig.endpoint.replace(/\/openai.*$/, ""),
22
91
  deployment: azureConfig.deployment,
23
92
  apiVersion: azureConfig.apiVersion,
24
93
  timeout: 30000,
25
94
  maxRetries: 0,
26
- });
95
+ };
96
+ if (useApiKey) {
97
+ clientOptions.apiKey = azureConfig.apiKey;
98
+ }
99
+ else {
100
+ const managedIdentityClientId = azureConfig.managedIdentityClientId || undefined;
101
+ clientOptions.azureADTokenProvider = createAzureTokenProvider(managedIdentityClientId);
102
+ }
103
+ const client = new openai_1.AzureOpenAI(clientOptions);
27
104
  let nativeInput = null;
28
105
  let nativeInstructions = "";
29
106
  return {
30
107
  id: "azure",
31
108
  model: azureConfig.modelName,
32
109
  client,
110
+ capabilities,
111
+ supportedReasoningEfforts: modelCaps.reasoningEffort,
33
112
  resetTurnState(messages) {
34
113
  const { instructions, input } = (0, streaming_1.toResponsesInput)(messages);
35
114
  nativeInput = input;
@@ -48,7 +127,7 @@ function createAzureProviderRuntime() {
48
127
  input: nativeInput,
49
128
  instructions: nativeInstructions,
50
129
  tools: (0, streaming_1.toResponsesTools)(request.activeTools),
51
- reasoning: { effort: "medium", summary: "detailed" },
130
+ reasoning: { effort: request.reasoningEffort ?? "medium", summary: "detailed" },
52
131
  stream: true,
53
132
  store: false,
54
133
  include: ["reasoning.encrypted_content"],
@@ -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
+ }
@@ -8,6 +8,7 @@ const openai_1 = __importDefault(require("openai"));
8
8
  const config_1 = require("../config");
9
9
  const runtime_1 = require("../../nerves/runtime");
10
10
  const streaming_1 = require("../streaming");
11
+ const model_capabilities_1 = require("../model-capabilities");
11
12
  function createMinimaxProviderRuntime() {
12
13
  (0, runtime_1.emitNervesEvent)({
13
14
  component: "engine",
@@ -19,6 +20,8 @@ function createMinimaxProviderRuntime() {
19
20
  if (!minimaxConfig.apiKey) {
20
21
  throw new Error("provider 'minimax' is selected in agent.json but providers.minimax.apiKey is missing in secrets.json.");
21
22
  }
23
+ // Registry consulted; MiniMax models return empty defaults (no capabilities to derive)
24
+ (0, model_capabilities_1.getModelCapabilities)(minimaxConfig.model);
22
25
  const client = new openai_1.default({
23
26
  apiKey: minimaxConfig.apiKey,
24
27
  baseURL: "https://api.minimaxi.chat/v1",
@@ -29,6 +32,7 @@ function createMinimaxProviderRuntime() {
29
32
  id: "minimax",
30
33
  model: minimaxConfig.model,
31
34
  client,
35
+ capabilities: new Set(),
32
36
  resetTurnState(_messages) {
33
37
  // No provider-owned turn state for chat-completions providers.
34
38
  },
@@ -9,6 +9,7 @@ const config_1 = require("../config");
9
9
  const identity_1 = require("../identity");
10
10
  const runtime_1 = require("../../nerves/runtime");
11
11
  const streaming_1 = require("../streaming");
12
+ const model_capabilities_1 = require("../model-capabilities");
12
13
  const OPENAI_CODEX_AUTH_FAILURE_MARKERS = [
13
14
  "authentication failed",
14
15
  "unauthorized",
@@ -27,11 +28,11 @@ function getOpenAICodexOAuthInstructions() {
27
28
  const agentName = getOpenAICodexAgentNameForGuidance();
28
29
  return [
29
30
  "Fix:",
30
- ` 1. Run \`npm run auth:openai-codex -- --agent ${agentName}\``,
31
- " (or run `codex login` and set the OAuth token manually)",
31
+ ` 1. Run \`ouro auth --agent ${agentName}\``,
32
32
  ` 2. Open ${getOpenAICodexSecretsPathForGuidance()}`,
33
33
  " 3. Confirm providers.openai-codex.oauthAccessToken is set",
34
34
  " 4. This provider uses chatgpt.com/backend-api/codex/responses (not api.openai.com/responses).",
35
+ " 5. After reauth, retry the failed ouro command or reconnect this session.",
35
36
  ].join("\n");
36
37
  }
37
38
  function getOpenAICodexReauthGuidance(reason) {
@@ -106,6 +107,12 @@ function createOpenAICodexProviderRuntime() {
106
107
  if (!chatgptAccountId) {
107
108
  throw new Error(getOpenAICodexReauthGuidance("OpenAI Codex OAuth access token is missing a chatgpt_account_id claim required for chatgpt.com/backend-api/codex."));
108
109
  }
110
+ const modelCaps = (0, model_capabilities_1.getModelCapabilities)(codexConfig.model);
111
+ const capabilities = new Set();
112
+ if (modelCaps.reasoningEffort)
113
+ capabilities.add("reasoning-effort");
114
+ if (modelCaps.phase)
115
+ capabilities.add("phase-annotation");
109
116
  const client = new openai_1.default({
110
117
  apiKey: token,
111
118
  baseURL: OPENAI_CODEX_BACKEND_BASE_URL,
@@ -123,6 +130,8 @@ function createOpenAICodexProviderRuntime() {
123
130
  id: "openai-codex",
124
131
  model: codexConfig.model,
125
132
  client,
133
+ capabilities,
134
+ supportedReasoningEfforts: modelCaps.reasoningEffort,
126
135
  resetTurnState(messages) {
127
136
  const { instructions, input } = (0, streaming_1.toResponsesInput)(messages);
128
137
  nativeInput = input;
@@ -141,7 +150,7 @@ function createOpenAICodexProviderRuntime() {
141
150
  input: nativeInput,
142
151
  instructions: nativeInstructions,
143
152
  tools: (0, streaming_1.toResponsesTools)(request.activeTools),
144
- reasoning: { effort: "medium", summary: "detailed" },
153
+ reasoning: { effort: request.reasoningEffort ?? "medium", summary: "detailed" },
145
154
  stream: true,
146
155
  store: false,
147
156
  include: ["reasoning.encrypted_content"],