@ouro.bot/cli 0.1.0-alpha.23 → 0.1.0-alpha.230

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 (241) hide show
  1. package/README.md +187 -191
  2. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
  3. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +1 -1
  4. package/changelog.json +1402 -1
  5. package/dist/arc/attention-types.js +8 -0
  6. package/dist/arc/cares.js +140 -0
  7. package/dist/arc/episodes.js +119 -0
  8. package/dist/arc/intentions.js +133 -0
  9. package/dist/arc/json-store.js +120 -0
  10. package/dist/arc/obligations.js +237 -0
  11. package/dist/arc/presence.js +185 -0
  12. package/dist/heart/active-work.js +832 -0
  13. package/dist/heart/agent-entry.js +37 -2
  14. package/dist/heart/auth/auth-flow.js +447 -0
  15. package/dist/heart/bridges/manager.js +358 -0
  16. package/dist/heart/bridges/state-machine.js +135 -0
  17. package/dist/heart/bridges/store.js +123 -0
  18. package/dist/heart/commitments.js +111 -0
  19. package/dist/heart/config-registry.js +305 -0
  20. package/dist/heart/config.js +63 -30
  21. package/dist/heart/core.js +601 -145
  22. package/dist/heart/cross-chat-delivery.js +131 -0
  23. package/dist/heart/daemon/agent-config-check.js +149 -0
  24. package/dist/heart/daemon/agent-discovery.js +81 -0
  25. package/dist/heart/daemon/agent-service.js +360 -0
  26. package/dist/heart/daemon/cadence.js +70 -0
  27. package/dist/heart/daemon/cli-defaults.js +557 -0
  28. package/dist/heart/daemon/cli-exec.js +1847 -0
  29. package/dist/heart/daemon/cli-parse.js +759 -0
  30. package/dist/heart/daemon/cli-render.js +316 -0
  31. package/dist/heart/daemon/cli-types.js +8 -0
  32. package/dist/heart/daemon/daemon-cli.js +29 -1163
  33. package/dist/heart/daemon/daemon-entry.js +202 -4
  34. package/dist/heart/daemon/daemon-health.js +136 -0
  35. package/dist/heart/daemon/daemon-runtime-sync.js +153 -12
  36. package/dist/heart/daemon/daemon-tombstone.js +123 -0
  37. package/dist/heart/daemon/daemon.js +489 -20
  38. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  39. package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
  40. package/dist/heart/daemon/inner-status.js +89 -0
  41. package/dist/heart/daemon/launchd.js +39 -8
  42. package/dist/heart/daemon/log-tailer.js +4 -3
  43. package/dist/heart/daemon/message-router.js +17 -8
  44. package/dist/heart/daemon/os-cron-deps.js +134 -0
  45. package/dist/heart/daemon/ouro-bot-entry.js +1 -1
  46. package/dist/heart/daemon/process-manager.js +123 -0
  47. package/dist/heart/daemon/run-hooks.js +2 -0
  48. package/dist/heart/daemon/runtime-logging.js +58 -15
  49. package/dist/heart/daemon/runtime-metadata.js +101 -0
  50. package/dist/heart/daemon/runtime-mode.js +67 -0
  51. package/dist/heart/daemon/safe-mode.js +161 -0
  52. package/dist/heart/daemon/sense-manager.js +72 -3
  53. package/dist/heart/daemon/session-id-resolver.js +131 -0
  54. package/dist/heart/daemon/skill-management-installer.js +94 -0
  55. package/dist/heart/daemon/socket-client.js +201 -0
  56. package/dist/heart/daemon/task-scheduler.js +3 -25
  57. package/dist/heart/daemon/thoughts.js +507 -0
  58. package/dist/heart/delegation.js +62 -0
  59. package/dist/heart/habits/habit-migration.js +181 -0
  60. package/dist/heart/habits/habit-parser.js +119 -0
  61. package/dist/heart/habits/habit-scheduler.js +369 -0
  62. package/dist/heart/{daemon → hatch}/hatch-flow.js +29 -119
  63. package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
  64. package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
  65. package/dist/heart/{daemon → hatch}/specialist-tools.js +49 -3
  66. package/dist/heart/identity.js +144 -55
  67. package/dist/heart/kicks.js +2 -20
  68. package/dist/heart/mcp/mcp-server.js +608 -0
  69. package/dist/heart/migrate-config.js +127 -0
  70. package/dist/heart/model-capabilities.js +48 -0
  71. package/dist/heart/outlook/outlook-http.js +439 -0
  72. package/dist/heart/outlook/outlook-read.js +1618 -0
  73. package/dist/heart/outlook/outlook-render.js +1032 -0
  74. package/dist/heart/outlook/outlook-types.js +7 -0
  75. package/dist/heart/outlook/outlook-view.js +194 -0
  76. package/dist/heart/progress-story.js +42 -0
  77. package/dist/heart/provider-failover.js +88 -0
  78. package/dist/heart/provider-ping.js +162 -0
  79. package/dist/heart/providers/anthropic-token.js +163 -0
  80. package/dist/heart/providers/anthropic.js +187 -41
  81. package/dist/heart/providers/azure.js +116 -10
  82. package/dist/heart/providers/github-copilot.js +157 -0
  83. package/dist/heart/providers/minimax.js +33 -3
  84. package/dist/heart/providers/openai-codex.js +51 -16
  85. package/dist/heart/session-activity.js +172 -0
  86. package/dist/heart/session-recall.js +216 -0
  87. package/dist/heart/start-of-turn-packet.js +330 -0
  88. package/dist/heart/streaming.js +36 -27
  89. package/dist/heart/sync.js +217 -0
  90. package/dist/heart/target-resolution.js +127 -0
  91. package/dist/heart/tempo.js +93 -0
  92. package/dist/heart/temporal-view.js +41 -0
  93. package/dist/heart/tool-activity-callbacks.js +36 -0
  94. package/dist/heart/tool-description.js +135 -0
  95. package/dist/heart/tool-loop.js +200 -0
  96. package/dist/heart/turn-context.js +358 -0
  97. package/dist/heart/turn-coordinator.js +28 -0
  98. package/dist/heart/versioning/ouro-bot-global-installer.js +128 -0
  99. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  100. package/dist/heart/{daemon → versioning}/ouro-path-installer.js +78 -35
  101. package/dist/heart/versioning/ouro-version-manager.js +171 -0
  102. package/dist/heart/{daemon → versioning}/staged-restart.js +30 -6
  103. package/dist/heart/{daemon → versioning}/update-checker.js +9 -1
  104. package/dist/heart/{daemon → versioning}/update-hooks.js +20 -2
  105. package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +41 -3
  106. package/dist/mind/associative-recall.js +126 -66
  107. package/dist/mind/bundle-manifest.js +8 -1
  108. package/dist/mind/context.js +125 -14
  109. package/dist/mind/{memory.js → diary.js} +73 -96
  110. package/dist/mind/embedding-provider.js +60 -0
  111. package/dist/mind/file-state.js +179 -0
  112. package/dist/mind/first-impressions.js +16 -2
  113. package/dist/mind/friends/channel.js +56 -0
  114. package/dist/mind/friends/group-context.js +144 -0
  115. package/dist/mind/friends/store-file.js +58 -3
  116. package/dist/mind/friends/trust-explanation.js +74 -0
  117. package/dist/mind/friends/types.js +8 -0
  118. package/dist/mind/journal-index.js +161 -0
  119. package/dist/mind/obligation-steering.js +221 -0
  120. package/dist/mind/pending.js +76 -9
  121. package/dist/mind/phrases.js +1 -0
  122. package/dist/mind/prompt.js +779 -109
  123. package/dist/mind/scrutiny.js +173 -0
  124. package/dist/mind/token-estimate.js +8 -12
  125. package/dist/nerves/coverage/audit.js +1 -1
  126. package/dist/nerves/coverage/file-completeness.js +47 -5
  127. package/dist/nerves/coverage/run-artifacts.js +1 -1
  128. package/dist/nerves/event-buffer.js +111 -0
  129. package/dist/nerves/index.js +64 -4
  130. package/dist/nerves/observation.js +20 -0
  131. package/dist/nerves/runtime.js +5 -1
  132. package/dist/outlook-ui/assets/index-Ck8agNeO.js +61 -0
  133. package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
  134. package/dist/outlook-ui/index.html +15 -0
  135. package/dist/repertoire/ado-client.js +15 -56
  136. package/dist/repertoire/ado-semantic.js +11 -5
  137. package/dist/repertoire/api-client.js +97 -0
  138. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  139. package/dist/repertoire/coding/context-pack.js +330 -0
  140. package/dist/repertoire/coding/feedback.js +197 -30
  141. package/dist/repertoire/coding/manager.js +149 -2
  142. package/dist/repertoire/coding/spawner.js +55 -9
  143. package/dist/repertoire/coding/tools.js +170 -7
  144. package/dist/repertoire/credential-access.js +449 -0
  145. package/dist/repertoire/github-client.js +14 -55
  146. package/dist/repertoire/graph-client.js +11 -52
  147. package/dist/repertoire/guardrails.js +342 -0
  148. package/dist/repertoire/mcp-client.js +254 -0
  149. package/dist/repertoire/mcp-manager.js +251 -0
  150. package/dist/repertoire/shell-sessions.js +133 -0
  151. package/dist/repertoire/skills.js +14 -23
  152. package/dist/repertoire/tasks/board.js +43 -5
  153. package/dist/repertoire/tasks/fix.js +182 -0
  154. package/dist/repertoire/tasks/index.js +28 -10
  155. package/dist/repertoire/tasks/lifecycle.js +2 -2
  156. package/dist/repertoire/tasks/parser.js +3 -2
  157. package/dist/repertoire/tasks/scanner.js +194 -37
  158. package/dist/repertoire/tasks/transitions.js +11 -28
  159. package/dist/repertoire/tools-base.js +35 -771
  160. package/dist/repertoire/tools-bluebubbles.js +94 -0
  161. package/dist/repertoire/tools-bridge.js +141 -0
  162. package/dist/repertoire/tools-config.js +266 -0
  163. package/dist/repertoire/tools-continuity.js +248 -0
  164. package/dist/repertoire/tools-credential.js +184 -0
  165. package/dist/repertoire/tools-files.js +342 -0
  166. package/dist/repertoire/tools-flow.js +62 -0
  167. package/dist/repertoire/tools-github.js +1 -6
  168. package/dist/repertoire/tools-memory.js +347 -0
  169. package/dist/repertoire/tools-session.js +740 -0
  170. package/dist/repertoire/tools-shell.js +120 -0
  171. package/dist/repertoire/tools-surface.js +242 -0
  172. package/dist/repertoire/tools-teams.js +12 -59
  173. package/dist/repertoire/tools-travel.js +125 -0
  174. package/dist/repertoire/tools.js +132 -124
  175. package/dist/repertoire/travel-api-client.js +214 -0
  176. package/dist/scripts/claude-code-hook.js +41 -0
  177. package/dist/scripts/claude-code-stop-hook.js +47 -0
  178. package/dist/senses/attention-queue.js +97 -0
  179. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +109 -10
  180. package/dist/senses/bluebubbles/entry.js +13 -0
  181. package/dist/senses/bluebubbles/inbound-log.js +109 -0
  182. package/dist/senses/bluebubbles/index.js +1359 -0
  183. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +119 -24
  184. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +13 -5
  185. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
  186. package/dist/senses/bluebubbles/runtime-state.js +109 -0
  187. package/dist/senses/bluebubbles/session-cleanup.js +72 -0
  188. package/dist/senses/cli/adapter.js +274 -0
  189. package/dist/senses/cli/ink-app.js +156 -0
  190. package/dist/senses/cli/inline-diff.js +64 -0
  191. package/dist/senses/cli/message-list.js +51 -0
  192. package/dist/senses/cli/ouro-tui.js +358 -0
  193. package/dist/senses/cli/spinner-imperative.js +135 -0
  194. package/dist/senses/cli/spinner.js +101 -0
  195. package/dist/senses/cli/status-line.js +60 -0
  196. package/dist/senses/cli/streaming-markdown.js +526 -0
  197. package/dist/senses/cli/tool-display.js +83 -0
  198. package/dist/senses/cli/tool-render.js +85 -0
  199. package/dist/senses/cli/tui-store.js +198 -0
  200. package/dist/senses/cli/virtual-list.js +35 -0
  201. package/dist/senses/cli-entry.js +1 -1
  202. package/dist/senses/cli-layout.js +187 -0
  203. package/dist/senses/cli.js +503 -244
  204. package/dist/senses/commands.js +65 -1
  205. package/dist/senses/continuity.js +94 -0
  206. package/dist/senses/habit-turn-message.js +108 -0
  207. package/dist/senses/inner-dialog-worker.js +112 -19
  208. package/dist/senses/inner-dialog.js +591 -84
  209. package/dist/senses/pipeline.js +559 -0
  210. package/dist/senses/shared-turn.js +196 -0
  211. package/dist/senses/surface-tool.js +68 -0
  212. package/dist/senses/teams.js +667 -120
  213. package/dist/senses/trust-gate.js +112 -2
  214. package/package.json +12 -6
  215. package/skills/browser-navigation.md +110 -0
  216. package/skills/configure-dev-tools.md +81 -0
  217. package/skills/travel-planning.md +138 -0
  218. package/dist/heart/daemon/subagent-installer.js +0 -134
  219. package/dist/senses/bluebubbles-entry.js +0 -11
  220. package/dist/senses/bluebubbles.js +0 -421
  221. package/dist/senses/debug-activity.js +0 -107
  222. package/subagents/README.md +0 -73
  223. package/subagents/work-doer.md +0 -235
  224. package/subagents/work-merger.md +0 -618
  225. package/subagents/work-planner.md +0 -382
  226. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  227. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  228. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  229. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  230. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  231. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  232. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  233. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  234. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  235. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  236. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  237. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
  238. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  239. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  240. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  241. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
@@ -0,0 +1,157 @@
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.classifyGithubCopilotError = classifyGithubCopilotError;
7
+ exports.createGithubCopilotProviderRuntime = createGithubCopilotProviderRuntime;
8
+ const openai_1 = __importDefault(require("openai"));
9
+ const config_1 = require("../config");
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 -- duplicated from shared provider utils, tested there @preserve */
14
+ function isNetworkError(error) {
15
+ const code = error.code || "";
16
+ if (["ECONNRESET", "ECONNREFUSED", "ENOTFOUND", "ETIMEDOUT", "EPIPE",
17
+ "EAI_AGAIN", "EHOSTUNREACH", "ENETUNREACH", "ECONNABORTED"].includes(code))
18
+ return true;
19
+ const msg = error.message || "";
20
+ return msg.includes("fetch failed") || msg.includes("socket hang up") || msg.includes("getaddrinfo");
21
+ }
22
+ /* v8 ignore stop */
23
+ /* v8 ignore start -- duplicated classification pattern, tested via provider unit tests @preserve */
24
+ function classifyGithubCopilotError(error) {
25
+ const status = error.status;
26
+ if (status === 401 || status === 403)
27
+ return "auth-failure";
28
+ if (status === 429)
29
+ return "rate-limit";
30
+ if (status && status >= 500)
31
+ return "server-error";
32
+ if (isNetworkError(error))
33
+ return "network-error";
34
+ return "unknown";
35
+ }
36
+ /* v8 ignore stop */
37
+ function createGithubCopilotProviderRuntime(model) {
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 = model.startsWith("claude");
52
+ const modelCaps = (0, model_capabilities_1.getModelCapabilities)(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,
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, request.eagerSettleStreaming);
92
+ }
93
+ catch (error) {
94
+ throw error instanceof Error ? error : new Error(String(error));
95
+ }
96
+ },
97
+ /* v8 ignore stop */
98
+ /* v8 ignore next 3 -- delegation: classification logic tested via classifyGithubCopilotError @preserve */
99
+ classifyError(error) {
100
+ return classifyGithubCopilotError(error);
101
+ },
102
+ };
103
+ }
104
+ // Responses API path (GPT models via Copilot)
105
+ let nativeInput = null;
106
+ let nativeInstructions = "";
107
+ return {
108
+ id: "github-copilot",
109
+ model,
110
+ client,
111
+ capabilities,
112
+ supportedReasoningEfforts: modelCaps.reasoningEffort,
113
+ /* v8 ignore start -- responses path: tested via mock assertions in github-copilot.test.ts @preserve */
114
+ resetTurnState(messages) {
115
+ const { instructions, input } = (0, streaming_1.toResponsesInput)(messages);
116
+ nativeInput = input;
117
+ nativeInstructions = instructions;
118
+ },
119
+ appendToolOutput(callId, output) {
120
+ if (!nativeInput)
121
+ return;
122
+ nativeInput.push({ type: "function_call_output", call_id: callId, output });
123
+ },
124
+ async streamTurn(request) {
125
+ if (!nativeInput)
126
+ this.resetTurnState(request.messages);
127
+ const params = {
128
+ model: this.model,
129
+ input: nativeInput,
130
+ instructions: nativeInstructions,
131
+ tools: (0, streaming_1.toResponsesTools)(request.activeTools),
132
+ reasoning: { effort: request.reasoningEffort ?? "medium", summary: "detailed" },
133
+ stream: true,
134
+ store: false,
135
+ include: ["reasoning.encrypted_content"],
136
+ };
137
+ if (request.traceId)
138
+ params.metadata = { trace_id: request.traceId };
139
+ if (request.toolChoiceRequired)
140
+ params.tool_choice = "required";
141
+ try {
142
+ const result = await (0, streaming_1.streamResponsesApi)(this.client, params, request.callbacks, request.signal, request.eagerSettleStreaming);
143
+ for (const item of result.outputItems)
144
+ nativeInput.push(item);
145
+ return result;
146
+ }
147
+ catch (error) {
148
+ throw error instanceof Error ? error : new Error(String(error));
149
+ }
150
+ },
151
+ /* v8 ignore stop */
152
+ /* v8 ignore next 3 -- delegation: classification logic tested via classifyGithubCopilotError @preserve */
153
+ classifyError(error) {
154
+ return classifyGithubCopilotError(error);
155
+ },
156
+ };
157
+ }
@@ -3,12 +3,36 @@ 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.classifyMinimaxError = classifyMinimaxError;
6
7
  exports.createMinimaxProviderRuntime = createMinimaxProviderRuntime;
7
8
  const openai_1 = __importDefault(require("openai"));
8
9
  const config_1 = require("../config");
9
10
  const runtime_1 = require("../../nerves/runtime");
11
+ /* v8 ignore start -- shared network error utility, tested via classification tests @preserve */
12
+ function isNetworkError(error) {
13
+ const code = error.code || "";
14
+ if (["ECONNRESET", "ECONNREFUSED", "ENOTFOUND", "ETIMEDOUT", "EPIPE",
15
+ "EAI_AGAIN", "EHOSTUNREACH", "ENETUNREACH", "ECONNABORTED"].includes(code))
16
+ return true;
17
+ const msg = error.message || "";
18
+ return msg.includes("fetch failed") || msg.includes("socket hang up") || msg.includes("getaddrinfo");
19
+ }
20
+ /* v8 ignore stop */
21
+ function classifyMinimaxError(error) {
22
+ const status = error.status;
23
+ if (status === 401 || status === 403)
24
+ return "auth-failure";
25
+ if (status === 429)
26
+ return "rate-limit";
27
+ if (status && status >= 500)
28
+ return "server-error";
29
+ if (isNetworkError(error))
30
+ return "network-error";
31
+ return "unknown";
32
+ }
10
33
  const streaming_1 = require("../streaming");
11
- function createMinimaxProviderRuntime() {
34
+ const model_capabilities_1 = require("../model-capabilities");
35
+ function createMinimaxProviderRuntime(model) {
12
36
  (0, runtime_1.emitNervesEvent)({
13
37
  component: "engine",
14
38
  event: "engine.provider_init",
@@ -19,6 +43,8 @@ function createMinimaxProviderRuntime() {
19
43
  if (!minimaxConfig.apiKey) {
20
44
  throw new Error("provider 'minimax' is selected in agent.json but providers.minimax.apiKey is missing in secrets.json.");
21
45
  }
46
+ // Registry consulted; MiniMax models return empty defaults (no capabilities to derive)
47
+ (0, model_capabilities_1.getModelCapabilities)(model);
22
48
  const client = new openai_1.default({
23
49
  apiKey: minimaxConfig.apiKey,
24
50
  baseURL: "https://api.minimaxi.chat/v1",
@@ -27,8 +53,9 @@ function createMinimaxProviderRuntime() {
27
53
  });
28
54
  return {
29
55
  id: "minimax",
30
- model: minimaxConfig.model,
56
+ model,
31
57
  client,
58
+ capabilities: new Set(),
32
59
  resetTurnState(_messages) {
33
60
  // No provider-owned turn state for chat-completions providers.
34
61
  },
@@ -47,7 +74,10 @@ function createMinimaxProviderRuntime() {
47
74
  params.metadata = { trace_id: request.traceId };
48
75
  if (request.toolChoiceRequired)
49
76
  params.tool_choice = "required";
50
- return (0, streaming_1.streamChatCompletion)(this.client, params, request.callbacks, request.signal);
77
+ return (0, streaming_1.streamChatCompletion)(this.client, params, request.callbacks, request.signal, request.eagerSettleStreaming);
78
+ },
79
+ classifyError(error) {
80
+ return classifyMinimaxError(error);
51
81
  },
52
82
  };
53
83
  }
@@ -3,12 +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.classifyOpenAICodexError = classifyOpenAICodexError;
6
7
  exports.createOpenAICodexProviderRuntime = createOpenAICodexProviderRuntime;
7
8
  const openai_1 = __importDefault(require("openai"));
8
9
  const config_1 = require("../config");
9
10
  const identity_1 = require("../identity");
10
11
  const runtime_1 = require("../../nerves/runtime");
11
12
  const streaming_1 = require("../streaming");
13
+ const model_capabilities_1 = require("../model-capabilities");
12
14
  const OPENAI_CODEX_AUTH_FAILURE_MARKERS = [
13
15
  "authentication failed",
14
16
  "unauthorized",
@@ -27,11 +29,11 @@ function getOpenAICodexOAuthInstructions() {
27
29
  const agentName = getOpenAICodexAgentNameForGuidance();
28
30
  return [
29
31
  "Fix:",
30
- ` 1. Run \`npm run auth:openai-codex -- --agent ${agentName}\``,
31
- " (or run `codex login` and set the OAuth token manually)",
32
+ ` 1. Run \`ouro auth --agent ${agentName}\``,
32
33
  ` 2. Open ${getOpenAICodexSecretsPathForGuidance()}`,
33
34
  " 3. Confirm providers.openai-codex.oauthAccessToken is set",
34
35
  " 4. This provider uses chatgpt.com/backend-api/codex/responses (not api.openai.com/responses).",
36
+ " 5. After reauth, retry the failed ouro command or reconnect this session.",
35
37
  ].join("\n");
36
38
  }
37
39
  function getOpenAICodexReauthGuidance(reason) {
@@ -41,6 +43,33 @@ function getOpenAICodexReauthGuidance(reason) {
41
43
  getOpenAICodexOAuthInstructions(),
42
44
  ].join("\n");
43
45
  }
46
+ /* v8 ignore start -- shared network error utility, tested via classification tests @preserve */
47
+ function isNetworkError(error) {
48
+ const code = error.code || "";
49
+ if (["ECONNRESET", "ECONNREFUSED", "ENOTFOUND", "ETIMEDOUT", "EPIPE",
50
+ "EAI_AGAIN", "EHOSTUNREACH", "ENETUNREACH", "ECONNABORTED"].includes(code))
51
+ return true;
52
+ const msg = error.message || "";
53
+ return msg.includes("fetch failed") || msg.includes("socket hang up") || msg.includes("getaddrinfo");
54
+ }
55
+ /* v8 ignore stop */
56
+ function classifyOpenAICodexError(error) {
57
+ const status = error.status;
58
+ if (status === 401 || status === 403 || isOpenAICodexAuthFailure(error))
59
+ return "auth-failure";
60
+ if (status === 429) {
61
+ const lower = error.message.toLowerCase();
62
+ if (lower.includes("usage") || lower.includes("quota") || lower.includes("exceeded your"))
63
+ return "usage-limit";
64
+ return "rate-limit";
65
+ }
66
+ if (status && status >= 500)
67
+ return "server-error";
68
+ if (isNetworkError(error))
69
+ return "network-error";
70
+ return "unknown";
71
+ }
72
+ /* v8 ignore start -- auth detection: only called from classifyOpenAICodexError which always passes Error @preserve */
44
73
  function isOpenAICodexAuthFailure(error) {
45
74
  if (!(error instanceof Error))
46
75
  return false;
@@ -50,13 +79,7 @@ function isOpenAICodexAuthFailure(error) {
50
79
  const lower = error.message.toLowerCase();
51
80
  return OPENAI_CODEX_AUTH_FAILURE_MARKERS.some((marker) => lower.includes(marker));
52
81
  }
53
- function withOpenAICodexAuthGuidance(error) {
54
- const base = error instanceof Error ? error.message : String(error);
55
- if (isOpenAICodexAuthFailure(error)) {
56
- return new Error(getOpenAICodexReauthGuidance(`OpenAI Codex authentication failed (${base}).`));
57
- }
58
- return error instanceof Error ? error : new Error(String(error));
59
- }
82
+ /* v8 ignore stop */
60
83
  function decodeJwtPayload(token) {
61
84
  const parts = token.split(".");
62
85
  if (parts.length < 2)
@@ -87,7 +110,7 @@ function getChatGPTAccountIdFromToken(token) {
87
110
  return "";
88
111
  return accountId.trim();
89
112
  }
90
- function createOpenAICodexProviderRuntime() {
113
+ function createOpenAICodexProviderRuntime(model) {
91
114
  (0, runtime_1.emitNervesEvent)({
92
115
  component: "engine",
93
116
  event: "engine.provider_init",
@@ -95,8 +118,8 @@ function createOpenAICodexProviderRuntime() {
95
118
  meta: { provider: "openai-codex" },
96
119
  });
97
120
  const codexConfig = (0, config_1.getOpenAICodexConfig)();
98
- if (!(codexConfig.model && codexConfig.oauthAccessToken)) {
99
- throw new Error(getOpenAICodexReauthGuidance("provider 'openai-codex' is selected in agent.json but providers.openai-codex.model/oauthAccessToken is incomplete in secrets.json."));
121
+ if (!codexConfig.oauthAccessToken) {
122
+ throw new Error(getOpenAICodexReauthGuidance("provider 'openai-codex' is selected in agent.json but providers.openai-codex.oauthAccessToken is missing in secrets.json."));
100
123
  }
101
124
  const token = codexConfig.oauthAccessToken.trim();
102
125
  if (!token) {
@@ -106,6 +129,12 @@ function createOpenAICodexProviderRuntime() {
106
129
  if (!chatgptAccountId) {
107
130
  throw new Error(getOpenAICodexReauthGuidance("OpenAI Codex OAuth access token is missing a chatgpt_account_id claim required for chatgpt.com/backend-api/codex."));
108
131
  }
132
+ const modelCaps = (0, model_capabilities_1.getModelCapabilities)(model);
133
+ const capabilities = new Set();
134
+ if (modelCaps.reasoningEffort)
135
+ capabilities.add("reasoning-effort");
136
+ if (modelCaps.phase)
137
+ capabilities.add("phase-annotation");
109
138
  const client = new openai_1.default({
110
139
  apiKey: token,
111
140
  baseURL: OPENAI_CODEX_BACKEND_BASE_URL,
@@ -121,8 +150,10 @@ function createOpenAICodexProviderRuntime() {
121
150
  let nativeInstructions = "";
122
151
  return {
123
152
  id: "openai-codex",
124
- model: codexConfig.model,
153
+ model,
125
154
  client,
155
+ capabilities,
156
+ supportedReasoningEfforts: modelCaps.reasoningEffort,
126
157
  resetTurnState(messages) {
127
158
  const { instructions, input } = (0, streaming_1.toResponsesInput)(messages);
128
159
  nativeInput = input;
@@ -141,7 +172,7 @@ function createOpenAICodexProviderRuntime() {
141
172
  input: nativeInput,
142
173
  instructions: nativeInstructions,
143
174
  tools: (0, streaming_1.toResponsesTools)(request.activeTools),
144
- reasoning: { effort: "medium", summary: "detailed" },
175
+ reasoning: { effort: request.reasoningEffort ?? "medium", summary: "detailed" },
145
176
  stream: true,
146
177
  store: false,
147
178
  include: ["reasoning.encrypted_content"],
@@ -149,14 +180,18 @@ function createOpenAICodexProviderRuntime() {
149
180
  if (request.toolChoiceRequired)
150
181
  params.tool_choice = "required";
151
182
  try {
152
- const result = await (0, streaming_1.streamResponsesApi)(this.client, params, request.callbacks, request.signal);
183
+ const result = await (0, streaming_1.streamResponsesApi)(this.client, params, request.callbacks, request.signal, request.eagerSettleStreaming);
153
184
  for (const item of result.outputItems)
154
185
  nativeInput.push(item);
155
186
  return result;
156
187
  }
157
188
  catch (error) {
158
- throw withOpenAICodexAuthGuidance(error);
189
+ throw error instanceof Error ? error : new Error(String(error));
159
190
  }
160
191
  },
192
+ /* v8 ignore next 3 -- delegation: classification logic tested via classifyOpenAICodexError @preserve */
193
+ classifyError(error) {
194
+ return classifyOpenAICodexError(error);
195
+ },
161
196
  };
162
197
  }
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.listSessionActivity = listSessionActivity;
37
+ exports.findFreshestFriendSession = findFreshestFriendSession;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const runtime_1 = require("../nerves/runtime");
41
+ const config_1 = require("./config");
42
+ const DEFAULT_ACTIVE_THRESHOLD_MS = 24 * 60 * 60 * 1000;
43
+ function activityPriority(source) {
44
+ return source === "friend-facing" ? 0 : 1;
45
+ }
46
+ function resolveFriendName(friendId, friendsDir, agentName) {
47
+ if (friendId === "self")
48
+ return agentName;
49
+ try {
50
+ const raw = fs.readFileSync(path.join(friendsDir, `${friendId}.json`), "utf-8");
51
+ const parsed = JSON.parse(raw);
52
+ return parsed.name ?? friendId;
53
+ }
54
+ catch {
55
+ return friendId;
56
+ }
57
+ }
58
+ function parseFriendActivity(sessionPath) {
59
+ let mtimeMs;
60
+ try {
61
+ mtimeMs = fs.statSync(sessionPath).mtimeMs;
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ try {
67
+ const raw = fs.readFileSync(sessionPath, "utf-8");
68
+ const parsed = JSON.parse(raw);
69
+ const explicit = parsed?.state?.lastFriendActivityAt;
70
+ if (typeof explicit === "string") {
71
+ const parsedMs = Date.parse(explicit);
72
+ if (Number.isFinite(parsedMs)) {
73
+ return {
74
+ lastActivityMs: parsedMs,
75
+ lastActivityAt: new Date(parsedMs).toISOString(),
76
+ activitySource: "friend-facing",
77
+ };
78
+ }
79
+ }
80
+ }
81
+ catch {
82
+ // fall back to file mtime below
83
+ }
84
+ return {
85
+ lastActivityMs: mtimeMs,
86
+ lastActivityAt: new Date(mtimeMs).toISOString(),
87
+ activitySource: "mtime-fallback",
88
+ };
89
+ }
90
+ function listSessionActivity(query) {
91
+ const { sessionsDir, friendsDir, agentName, activeThresholdMs = DEFAULT_ACTIVE_THRESHOLD_MS, nowMs = Date.now(), currentSession = null, } = query;
92
+ (0, runtime_1.emitNervesEvent)({
93
+ component: "daemon",
94
+ event: "daemon.session_activity_scan",
95
+ message: "scanning session activity",
96
+ meta: {
97
+ sessionsDir,
98
+ currentSession: currentSession ? `${currentSession.friendId}/${currentSession.channel}/${currentSession.key}` : null,
99
+ },
100
+ });
101
+ if (!fs.existsSync(sessionsDir))
102
+ return [];
103
+ const results = [];
104
+ let friendDirs;
105
+ try {
106
+ friendDirs = fs.readdirSync(sessionsDir);
107
+ }
108
+ catch {
109
+ return [];
110
+ }
111
+ for (const friendId of friendDirs) {
112
+ const friendPath = path.join(sessionsDir, friendId);
113
+ let channels;
114
+ try {
115
+ channels = fs.readdirSync(friendPath);
116
+ }
117
+ catch {
118
+ continue;
119
+ }
120
+ for (const channel of channels) {
121
+ const channelPath = path.join(friendPath, channel);
122
+ let keys;
123
+ try {
124
+ keys = fs.readdirSync(channelPath);
125
+ }
126
+ catch {
127
+ continue;
128
+ }
129
+ for (const keyFile of keys) {
130
+ if (!keyFile.endsWith(".json"))
131
+ continue;
132
+ const key = keyFile.replace(/\.json$/, "");
133
+ // Compare with sanitizeKey on both sides — session keys from the filesystem
134
+ // are already sanitized (colons → underscores), but the canonical key from
135
+ // the pipeline may still have colons (e.g. "chat:any" vs "chat_any").
136
+ if (currentSession && friendId === currentSession.friendId && channel === currentSession.channel && (0, config_1.sanitizeKey)(key) === (0, config_1.sanitizeKey)(currentSession.key)) {
137
+ continue;
138
+ }
139
+ const sessionPath = path.join(channelPath, keyFile);
140
+ const activity = parseFriendActivity(sessionPath);
141
+ if (!activity)
142
+ continue;
143
+ if (nowMs - activity.lastActivityMs > activeThresholdMs)
144
+ continue;
145
+ results.push({
146
+ friendId,
147
+ friendName: resolveFriendName(friendId, friendsDir, agentName),
148
+ channel,
149
+ key,
150
+ sessionPath,
151
+ lastActivityAt: activity.lastActivityAt,
152
+ lastActivityMs: activity.lastActivityMs,
153
+ activitySource: activity.activitySource,
154
+ });
155
+ }
156
+ }
157
+ }
158
+ return results.sort((a, b) => {
159
+ const sourceDiff = activityPriority(a.activitySource) - activityPriority(b.activitySource);
160
+ if (sourceDiff !== 0)
161
+ return sourceDiff;
162
+ return b.lastActivityMs - a.lastActivityMs;
163
+ });
164
+ }
165
+ function findFreshestFriendSession(query) {
166
+ const { activeOnly = false, activeThresholdMs = DEFAULT_ACTIVE_THRESHOLD_MS, nowMs, ...rest } = query;
167
+ const currentSession = rest.currentSession ?? null;
168
+ const all = activeOnly
169
+ ? listSessionActivity({ ...rest, activeThresholdMs, nowMs, currentSession })
170
+ : listSessionActivity({ ...rest, activeThresholdMs: Number.MAX_SAFE_INTEGER, nowMs, currentSession });
171
+ return all.find((entry) => entry.friendId === query.friendId) ?? null;
172
+ }