@ouro.bot/cli 0.1.0-alpha.11 → 0.1.0-alpha.111

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 (129) hide show
  1. package/AdoptionSpecialist.ouro/psyche/SOUL.md +2 -2
  2. package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
  3. package/README.md +147 -205
  4. package/assets/ouroboros.png +0 -0
  5. package/changelog.json +672 -0
  6. package/dist/heart/active-work.js +622 -0
  7. package/dist/heart/bridges/manager.js +358 -0
  8. package/dist/heart/bridges/state-machine.js +135 -0
  9. package/dist/heart/bridges/store.js +123 -0
  10. package/dist/heart/commitments.js +105 -0
  11. package/dist/heart/config.js +68 -23
  12. package/dist/heart/core.js +492 -96
  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 +430 -0
  16. package/dist/heart/daemon/daemon-cli.js +1711 -208
  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 +59 -15
  30. package/dist/heart/daemon/ouro-uti.js +11 -2
  31. package/dist/heart/daemon/ouro-version-manager.js +171 -0
  32. package/dist/heart/daemon/process-manager.js +14 -1
  33. package/dist/heart/daemon/run-hooks.js +37 -0
  34. package/dist/heart/daemon/runtime-logging.js +58 -15
  35. package/dist/heart/daemon/runtime-metadata.js +219 -0
  36. package/dist/heart/daemon/runtime-mode.js +67 -0
  37. package/dist/heart/daemon/sense-manager.js +307 -0
  38. package/dist/heart/daemon/skill-management-installer.js +94 -0
  39. package/dist/heart/daemon/socket-client.js +202 -0
  40. package/dist/heart/daemon/specialist-orchestrator.js +37 -94
  41. package/dist/heart/daemon/specialist-prompt.js +50 -12
  42. package/dist/heart/daemon/specialist-tools.js +211 -60
  43. package/dist/heart/daemon/staged-restart.js +114 -0
  44. package/dist/heart/daemon/thoughts.js +507 -0
  45. package/dist/heart/daemon/update-checker.js +111 -0
  46. package/dist/heart/daemon/update-hooks.js +138 -0
  47. package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
  48. package/dist/heart/delegation.js +62 -0
  49. package/dist/heart/identity.js +126 -21
  50. package/dist/heart/kicks.js +1 -19
  51. package/dist/heart/model-capabilities.js +48 -0
  52. package/dist/heart/obligations.js +197 -0
  53. package/dist/heart/progress-story.js +42 -0
  54. package/dist/heart/providers/anthropic.js +74 -9
  55. package/dist/heart/providers/azure.js +87 -8
  56. package/dist/heart/providers/github-copilot.js +149 -0
  57. package/dist/heart/providers/minimax.js +5 -1
  58. package/dist/heart/providers/openai-codex.js +13 -4
  59. package/dist/heart/safe-workspace.js +381 -0
  60. package/dist/heart/sense-truth.js +61 -0
  61. package/dist/heart/session-activity.js +169 -0
  62. package/dist/heart/session-recall.js +216 -0
  63. package/dist/heart/streaming.js +108 -24
  64. package/dist/heart/target-resolution.js +123 -0
  65. package/dist/heart/tool-loop.js +194 -0
  66. package/dist/heart/turn-coordinator.js +28 -0
  67. package/dist/mind/associative-recall.js +14 -2
  68. package/dist/mind/bundle-manifest.js +70 -0
  69. package/dist/mind/context.js +60 -14
  70. package/dist/mind/first-impressions.js +16 -2
  71. package/dist/mind/friends/channel.js +35 -0
  72. package/dist/mind/friends/group-context.js +144 -0
  73. package/dist/mind/friends/store-file.js +19 -0
  74. package/dist/mind/friends/trust-explanation.js +74 -0
  75. package/dist/mind/friends/types.js +8 -0
  76. package/dist/mind/memory.js +27 -26
  77. package/dist/mind/obligation-steering.js +221 -0
  78. package/dist/mind/pending.js +76 -9
  79. package/dist/mind/phrases.js +1 -0
  80. package/dist/mind/prompt.js +527 -77
  81. package/dist/mind/token-estimate.js +8 -12
  82. package/dist/nerves/cli-logging.js +15 -2
  83. package/dist/nerves/coverage/run-artifacts.js +1 -1
  84. package/dist/nerves/index.js +12 -0
  85. package/dist/repertoire/ado-client.js +4 -2
  86. package/dist/repertoire/coding/context-pack.js +254 -0
  87. package/dist/repertoire/coding/feedback.js +301 -0
  88. package/dist/repertoire/coding/index.js +4 -1
  89. package/dist/repertoire/coding/manager.js +210 -4
  90. package/dist/repertoire/coding/spawner.js +39 -9
  91. package/dist/repertoire/coding/tools.js +171 -4
  92. package/dist/repertoire/data/ado-endpoints.json +188 -0
  93. package/dist/repertoire/guardrails.js +290 -0
  94. package/dist/repertoire/mcp-client.js +254 -0
  95. package/dist/repertoire/mcp-manager.js +195 -0
  96. package/dist/repertoire/skills.js +3 -26
  97. package/dist/repertoire/tasks/board.js +12 -0
  98. package/dist/repertoire/tasks/index.js +23 -9
  99. package/dist/repertoire/tasks/transitions.js +1 -2
  100. package/dist/repertoire/tools-base.js +925 -250
  101. package/dist/repertoire/tools-bluebubbles.js +93 -0
  102. package/dist/repertoire/tools-teams.js +58 -25
  103. package/dist/repertoire/tools.js +106 -53
  104. package/dist/senses/bluebubbles-client.js +210 -5
  105. package/dist/senses/bluebubbles-entry.js +2 -0
  106. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  107. package/dist/senses/bluebubbles-media.js +339 -0
  108. package/dist/senses/bluebubbles-model.js +12 -4
  109. package/dist/senses/bluebubbles-mutation-log.js +45 -5
  110. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  111. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  112. package/dist/senses/bluebubbles.js +900 -45
  113. package/dist/senses/cli-layout.js +187 -0
  114. package/dist/senses/cli.js +448 -170
  115. package/dist/senses/continuity.js +94 -0
  116. package/dist/senses/debug-activity.js +154 -0
  117. package/dist/senses/inner-dialog-worker.js +47 -18
  118. package/dist/senses/inner-dialog.js +388 -83
  119. package/dist/senses/pipeline.js +334 -0
  120. package/dist/senses/teams.js +577 -129
  121. package/dist/senses/trust-gate.js +112 -2
  122. package/package.json +14 -3
  123. package/subagents/README.md +4 -70
  124. package/dist/heart/daemon/specialist-session.js +0 -177
  125. package/dist/heart/daemon/subagent-installer.js +0 -125
  126. package/dist/inner-worker-entry.js +0 -4
  127. package/subagents/work-doer.md +0 -233
  128. package/subagents/work-merger.md +0 -624
  129. package/subagents/work-planner.md +0 -373
@@ -0,0 +1,169 @@
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 DEFAULT_ACTIVE_THRESHOLD_MS = 24 * 60 * 60 * 1000;
42
+ function activityPriority(source) {
43
+ return source === "friend-facing" ? 0 : 1;
44
+ }
45
+ function resolveFriendName(friendId, friendsDir, agentName) {
46
+ if (friendId === "self")
47
+ return agentName;
48
+ try {
49
+ const raw = fs.readFileSync(path.join(friendsDir, `${friendId}.json`), "utf-8");
50
+ const parsed = JSON.parse(raw);
51
+ return parsed.name ?? friendId;
52
+ }
53
+ catch {
54
+ return friendId;
55
+ }
56
+ }
57
+ function parseFriendActivity(sessionPath) {
58
+ let mtimeMs;
59
+ try {
60
+ mtimeMs = fs.statSync(sessionPath).mtimeMs;
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ try {
66
+ const raw = fs.readFileSync(sessionPath, "utf-8");
67
+ const parsed = JSON.parse(raw);
68
+ const explicit = parsed?.state?.lastFriendActivityAt;
69
+ if (typeof explicit === "string") {
70
+ const parsedMs = Date.parse(explicit);
71
+ if (Number.isFinite(parsedMs)) {
72
+ return {
73
+ lastActivityMs: parsedMs,
74
+ lastActivityAt: new Date(parsedMs).toISOString(),
75
+ activitySource: "friend-facing",
76
+ };
77
+ }
78
+ }
79
+ }
80
+ catch {
81
+ // fall back to file mtime below
82
+ }
83
+ return {
84
+ lastActivityMs: mtimeMs,
85
+ lastActivityAt: new Date(mtimeMs).toISOString(),
86
+ activitySource: "mtime-fallback",
87
+ };
88
+ }
89
+ function listSessionActivity(query) {
90
+ const { sessionsDir, friendsDir, agentName, activeThresholdMs = DEFAULT_ACTIVE_THRESHOLD_MS, currentSession = null, } = query;
91
+ (0, runtime_1.emitNervesEvent)({
92
+ component: "daemon",
93
+ event: "daemon.session_activity_scan",
94
+ message: "scanning session activity",
95
+ meta: {
96
+ sessionsDir,
97
+ currentSession: currentSession ? `${currentSession.friendId}/${currentSession.channel}/${currentSession.key}` : null,
98
+ },
99
+ });
100
+ if (!fs.existsSync(sessionsDir))
101
+ return [];
102
+ const now = Date.now();
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
+ if (currentSession && friendId === currentSession.friendId && channel === currentSession.channel && key === currentSession.key) {
134
+ continue;
135
+ }
136
+ const sessionPath = path.join(channelPath, keyFile);
137
+ const activity = parseFriendActivity(sessionPath);
138
+ if (!activity)
139
+ continue;
140
+ if (now - activity.lastActivityMs > activeThresholdMs)
141
+ continue;
142
+ results.push({
143
+ friendId,
144
+ friendName: resolveFriendName(friendId, friendsDir, agentName),
145
+ channel,
146
+ key,
147
+ sessionPath,
148
+ lastActivityAt: activity.lastActivityAt,
149
+ lastActivityMs: activity.lastActivityMs,
150
+ activitySource: activity.activitySource,
151
+ });
152
+ }
153
+ }
154
+ }
155
+ return results.sort((a, b) => {
156
+ const sourceDiff = activityPriority(a.activitySource) - activityPriority(b.activitySource);
157
+ if (sourceDiff !== 0)
158
+ return sourceDiff;
159
+ return b.lastActivityMs - a.lastActivityMs;
160
+ });
161
+ }
162
+ function findFreshestFriendSession(query) {
163
+ const { activeOnly = false, activeThresholdMs = DEFAULT_ACTIVE_THRESHOLD_MS, ...rest } = query;
164
+ const currentSession = rest.currentSession ?? null;
165
+ const all = activeOnly
166
+ ? listSessionActivity({ ...rest, activeThresholdMs, currentSession })
167
+ : listSessionActivity({ ...rest, activeThresholdMs: Number.MAX_SAFE_INTEGER, currentSession });
168
+ return all.find((entry) => entry.friendId === query.friendId) ?? null;
169
+ }
@@ -0,0 +1,216 @@
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.recallSession = recallSession;
37
+ exports.searchSessionTranscript = searchSessionTranscript;
38
+ const fs = __importStar(require("fs"));
39
+ const runtime_1 = require("../nerves/runtime");
40
+ function normalizeContent(content) {
41
+ if (typeof content === "string")
42
+ return content;
43
+ if (!Array.isArray(content))
44
+ return "";
45
+ return content
46
+ .map((part) => (part && typeof part === "object" && "type" in part && part.type === "text" && "text" in part
47
+ ? String(part.text ?? "")
48
+ : ""))
49
+ .filter((text) => text.length > 0)
50
+ .join("");
51
+ }
52
+ function normalizeSessionMessages(messages) {
53
+ if (!Array.isArray(messages))
54
+ return [];
55
+ return messages
56
+ .map((message) => {
57
+ const record = message && typeof message === "object" ? message : {};
58
+ return {
59
+ role: typeof record.role === "string" ? record.role : "",
60
+ content: normalizeContent(record.content),
61
+ };
62
+ })
63
+ .filter((message) => message.role !== "system" && message.content.length > 0);
64
+ }
65
+ function buildSummaryInstruction(friendId, channel, trustLevel) {
66
+ if (friendId === "self" && channel === "inner") {
67
+ return "summarize this session transcript fully and transparently. this is my own inner dialog — include all details, decisions, and reasoning.";
68
+ }
69
+ return `summarize this session transcript. the person asking has trust level: ${trustLevel}. family=full transparency, friend=share work and general topics but protect other people's identities, acquaintance=very guarded minimal disclosure.`;
70
+ }
71
+ function clip(text, limit = 160) {
72
+ const compact = text.replace(/\s+/g, " ").trim();
73
+ return compact.length > limit ? compact.slice(0, limit - 1) + "…" : compact;
74
+ }
75
+ function buildSnapshot(summary, tailMessages) {
76
+ const lines = [`recent focus: ${clip(summary, 240)}`];
77
+ const latestUser = [...tailMessages].reverse().find((message) => message.role === "user")?.content;
78
+ const latestAssistant = [...tailMessages].reverse().find((message) => message.role === "assistant")?.content;
79
+ if (latestUser) {
80
+ lines.push(`latest user: ${clip(latestUser)}`);
81
+ }
82
+ if (latestAssistant) {
83
+ lines.push(`latest assistant: ${clip(latestAssistant)}`);
84
+ }
85
+ return lines.join("\n");
86
+ }
87
+ function buildSearchSnapshot(query, messages, includeLatestTurn = true) {
88
+ const lines = [`history query: "${clip(query, 120)}"`];
89
+ if (!includeLatestTurn) {
90
+ return lines.join("\n");
91
+ }
92
+ const latestUser = [...messages].reverse().find((message) => message.role === "user")?.content;
93
+ const latestAssistant = [...messages].reverse().find((message) => message.role === "assistant")?.content;
94
+ if (latestUser) {
95
+ lines.push(`latest user: ${clip(latestUser)}`);
96
+ }
97
+ if (latestAssistant) {
98
+ lines.push(`latest assistant: ${clip(latestAssistant)}`);
99
+ }
100
+ return lines.join("\n");
101
+ }
102
+ function buildSearchExcerpts(messages, query, maxMatches) {
103
+ const normalizedQuery = query.trim().toLowerCase();
104
+ if (!normalizedQuery)
105
+ return [];
106
+ const candidates = [];
107
+ let lastMatchIndex = -2;
108
+ for (let i = 0; i < messages.length; i++) {
109
+ if (!messages[i].content.toLowerCase().includes(normalizedQuery))
110
+ continue;
111
+ if (i <= lastMatchIndex + 1)
112
+ continue;
113
+ lastMatchIndex = i;
114
+ const start = Math.max(0, i - 1);
115
+ const end = Math.min(messages.length, i + 2);
116
+ const excerpt = messages
117
+ .slice(start, end)
118
+ .map((message) => `[${message.role}] ${clip(message.content, 200)}`)
119
+ .join("\n");
120
+ const score = messages
121
+ .slice(start, end)
122
+ .filter((message) => message.content.toLowerCase().includes(normalizedQuery))
123
+ .length;
124
+ candidates.push({ excerpt, score, index: i });
125
+ }
126
+ const seen = new Set();
127
+ return candidates
128
+ .sort((a, b) => b.score - a.score || a.index - b.index)
129
+ .filter((candidate) => {
130
+ if (seen.has(candidate.excerpt))
131
+ return false;
132
+ seen.add(candidate.excerpt);
133
+ return true;
134
+ })
135
+ .slice(0, maxMatches)
136
+ .map((candidate) => candidate.excerpt);
137
+ }
138
+ async function recallSession(options) {
139
+ (0, runtime_1.emitNervesEvent)({
140
+ component: "daemon",
141
+ event: "daemon.session_recall",
142
+ message: "recalling session transcript tail",
143
+ meta: {
144
+ friendId: options.friendId,
145
+ channel: options.channel,
146
+ key: options.key,
147
+ messageCount: options.messageCount,
148
+ },
149
+ });
150
+ let raw;
151
+ try {
152
+ raw = fs.readFileSync(options.sessionPath, "utf-8");
153
+ }
154
+ catch {
155
+ return { kind: "missing" };
156
+ }
157
+ const parsed = JSON.parse(raw);
158
+ const tailMessages = normalizeSessionMessages(parsed.messages).slice(-options.messageCount);
159
+ if (tailMessages.length === 0) {
160
+ return { kind: "empty" };
161
+ }
162
+ const transcript = tailMessages
163
+ .map((message) => `[${message.role}] ${message.content}`)
164
+ .join("\n");
165
+ const summary = options.summarize
166
+ ? await options.summarize(transcript, buildSummaryInstruction(options.friendId, options.channel, options.trustLevel ?? "family"))
167
+ : transcript;
168
+ return {
169
+ kind: "ok",
170
+ transcript,
171
+ summary,
172
+ snapshot: buildSnapshot(summary, tailMessages),
173
+ tailMessages,
174
+ };
175
+ }
176
+ async function searchSessionTranscript(options) {
177
+ (0, runtime_1.emitNervesEvent)({
178
+ component: "daemon",
179
+ event: "daemon.session_search",
180
+ message: "searching session transcript",
181
+ meta: {
182
+ friendId: options.friendId,
183
+ channel: options.channel,
184
+ key: options.key,
185
+ query: options.query,
186
+ maxMatches: options.maxMatches ?? 5,
187
+ },
188
+ });
189
+ let raw;
190
+ try {
191
+ raw = fs.readFileSync(options.sessionPath, "utf-8");
192
+ }
193
+ catch {
194
+ return { kind: "missing" };
195
+ }
196
+ const parsed = JSON.parse(raw);
197
+ const messages = normalizeSessionMessages(parsed.messages);
198
+ if (messages.length === 0) {
199
+ return { kind: "empty" };
200
+ }
201
+ const query = options.query.trim();
202
+ const matches = buildSearchExcerpts(messages, query, options.maxMatches ?? 5);
203
+ if (matches.length === 0) {
204
+ return {
205
+ kind: "no_match",
206
+ query,
207
+ snapshot: buildSearchSnapshot(query, messages),
208
+ };
209
+ }
210
+ return {
211
+ kind: "ok",
212
+ query,
213
+ snapshot: buildSearchSnapshot(query, messages, false),
214
+ matches,
215
+ };
216
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FinalAnswerParser = void 0;
3
+ exports.FinalAnswerStreamer = exports.FinalAnswerParser = void 0;
4
4
  exports.toResponsesInput = toResponsesInput;
5
5
  exports.toResponsesTools = toResponsesTools;
6
6
  exports.streamChatCompletion = streamChatCompletion;
@@ -77,6 +77,95 @@ class FinalAnswerParser {
77
77
  }
78
78
  }
79
79
  exports.FinalAnswerParser = FinalAnswerParser;
80
+ // Shared helper: wraps FinalAnswerParser with onClearText + onTextChunk wiring.
81
+ // Used by all streaming providers (Chat Completions, Responses API, Anthropic)
82
+ // so the eager-match streaming pattern lives in one place.
83
+ class FinalAnswerStreamer {
84
+ parser = new FinalAnswerParser();
85
+ _detected = false;
86
+ callbacks;
87
+ enabled;
88
+ constructor(callbacks, enabled = true) {
89
+ this.callbacks = callbacks;
90
+ this.enabled = enabled;
91
+ }
92
+ get detected() { return this._detected; }
93
+ get streamed() { return this.parser.active; }
94
+ /** Mark final_answer as detected. Calls onClearText on the callbacks. */
95
+ activate() {
96
+ if (!this.enabled)
97
+ return;
98
+ if (this._detected)
99
+ return;
100
+ this._detected = true;
101
+ this.callbacks.onClearText?.();
102
+ }
103
+ /** Feed an argument delta through the parser. Emits text via onTextChunk. */
104
+ processDelta(delta) {
105
+ if (!this.enabled)
106
+ return;
107
+ if (!this._detected)
108
+ return;
109
+ const text = this.parser.process(delta);
110
+ if (text)
111
+ this.callbacks.onTextChunk(text);
112
+ }
113
+ }
114
+ exports.FinalAnswerStreamer = FinalAnswerStreamer;
115
+ function toResponsesUserContent(content) {
116
+ if (typeof content === "string") {
117
+ return content;
118
+ }
119
+ if (!Array.isArray(content)) {
120
+ return "";
121
+ }
122
+ const parts = [];
123
+ for (const part of content) {
124
+ if (!part || typeof part !== "object") {
125
+ continue;
126
+ }
127
+ if (part.type === "text" && typeof part.text === "string") {
128
+ parts.push({ type: "input_text", text: part.text });
129
+ continue;
130
+ }
131
+ if (part.type === "image_url") {
132
+ const imageUrl = typeof part.image_url?.url === "string" ? part.image_url.url : "";
133
+ if (!imageUrl)
134
+ continue;
135
+ parts.push({
136
+ type: "input_image",
137
+ image_url: imageUrl,
138
+ detail: part.image_url?.detail ?? "auto",
139
+ });
140
+ continue;
141
+ }
142
+ if (part.type === "input_audio" &&
143
+ typeof part.input_audio?.data === "string" &&
144
+ (part.input_audio.format === "mp3" || part.input_audio.format === "wav")) {
145
+ parts.push({
146
+ type: "input_audio",
147
+ input_audio: {
148
+ data: part.input_audio.data,
149
+ format: part.input_audio.format,
150
+ },
151
+ });
152
+ continue;
153
+ }
154
+ if (part.type === "file") {
155
+ const fileRecord = { type: "input_file" };
156
+ if (typeof part.file?.file_data === "string")
157
+ fileRecord.file_data = part.file.file_data;
158
+ if (typeof part.file?.file_id === "string")
159
+ fileRecord.file_id = part.file.file_id;
160
+ if (typeof part.file?.filename === "string")
161
+ fileRecord.filename = part.file.filename;
162
+ if (typeof part.file?.file_data === "string" || typeof part.file?.file_id === "string") {
163
+ parts.push(fileRecord);
164
+ }
165
+ }
166
+ }
167
+ return parts.length > 0 ? parts : "";
168
+ }
80
169
  function toResponsesInput(messages) {
81
170
  let instructions = "";
82
171
  const input = [];
@@ -90,7 +179,7 @@ function toResponsesInput(messages) {
90
179
  }
91
180
  if (msg.role === "user") {
92
181
  const u = msg;
93
- input.push({ role: "user", content: typeof u.content === "string" ? u.content : "" });
182
+ input.push({ role: "user", content: toResponsesUserContent(u.content) });
94
183
  continue;
95
184
  }
96
185
  if (msg.role === "assistant") {
@@ -102,7 +191,10 @@ function toResponsesInput(messages) {
102
191
  }
103
192
  }
104
193
  if (a.content) {
105
- input.push({ role: "assistant", content: typeof a.content === "string" ? a.content : "" });
194
+ const assistantItem = { role: "assistant", content: typeof a.content === "string" ? a.content : "" };
195
+ if (a.phase)
196
+ assistantItem.phase = a.phase;
197
+ input.push(assistantItem);
106
198
  }
107
199
  if (a.tool_calls) {
108
200
  for (const tc of a.tool_calls) {
@@ -141,7 +233,7 @@ function toResponsesTools(ccTools) {
141
233
  strict: false,
142
234
  }));
143
235
  }
144
- async function streamChatCompletion(client, createParams, callbacks, signal) {
236
+ async function streamChatCompletion(client, createParams, callbacks, signal, eagerFinalAnswerStreaming = true) {
145
237
  (0, runtime_1.emitNervesEvent)({
146
238
  component: "engine",
147
239
  event: "engine.stream_start",
@@ -155,8 +247,7 @@ async function streamChatCompletion(client, createParams, callbacks, signal) {
155
247
  let toolCalls = {};
156
248
  let streamStarted = false;
157
249
  let usage;
158
- const answerParser = new FinalAnswerParser();
159
- let finalAnswerDetected = false;
250
+ const answerStreamer = new FinalAnswerStreamer(callbacks, eagerFinalAnswerStreaming);
160
251
  // State machine for parsing inline <think> tags (MiniMax pattern)
161
252
  let contentBuf = "";
162
253
  let inThinkTag = false;
@@ -274,21 +365,18 @@ async function streamChatCompletion(client, createParams, callbacks, signal) {
274
365
  // Detect final_answer tool call on first name delta.
275
366
  // Only activate streaming if this is the sole tool call (index 0
276
367
  // and no other indices seen). Mixed calls are rejected by core.ts.
277
- if (tc.function.name === "final_answer" && !finalAnswerDetected
368
+ if (tc.function.name === "final_answer" && !answerStreamer.detected
278
369
  && tc.index === 0 && Object.keys(toolCalls).length === 1) {
279
- finalAnswerDetected = true;
280
- callbacks.onClearText?.();
370
+ answerStreamer.activate();
281
371
  }
282
372
  }
283
373
  if (tc.function?.arguments) {
284
374
  toolCalls[tc.index].arguments += tc.function.arguments;
285
375
  // Feed final_answer argument deltas to the parser for progressive
286
376
  // streaming, but only when it appears to be the sole tool call.
287
- if (finalAnswerDetected && toolCalls[tc.index].name === "final_answer"
377
+ if (answerStreamer.detected && toolCalls[tc.index].name === "final_answer"
288
378
  && Object.keys(toolCalls).length === 1) {
289
- const text = answerParser.process(tc.function.arguments);
290
- if (text)
291
- callbacks.onTextChunk(text);
379
+ answerStreamer.processDelta(tc.function.arguments);
292
380
  }
293
381
  }
294
382
  }
@@ -302,10 +390,10 @@ async function streamChatCompletion(client, createParams, callbacks, signal) {
302
390
  toolCalls: Object.values(toolCalls),
303
391
  outputItems: [],
304
392
  usage,
305
- finalAnswerStreamed: answerParser.active,
393
+ finalAnswerStreamed: answerStreamer.streamed,
306
394
  };
307
395
  }
308
- async function streamResponsesApi(client, createParams, callbacks, signal) {
396
+ async function streamResponsesApi(client, createParams, callbacks, signal, eagerFinalAnswerStreaming = true) {
309
397
  (0, runtime_1.emitNervesEvent)({
310
398
  component: "engine",
311
399
  event: "engine.stream_start",
@@ -320,9 +408,8 @@ async function streamResponsesApi(client, createParams, callbacks, signal) {
320
408
  const outputItems = [];
321
409
  let currentToolCall = null;
322
410
  let usage;
323
- const answerParser = new FinalAnswerParser();
411
+ const answerStreamer = new FinalAnswerStreamer(callbacks, eagerFinalAnswerStreaming);
324
412
  let functionCallCount = 0;
325
- let finalAnswerDetected = false;
326
413
  for await (const event of response) {
327
414
  if (signal?.aborted)
328
415
  break;
@@ -355,8 +442,7 @@ async function streamResponsesApi(client, createParams, callbacks, signal) {
355
442
  // Only activate when this is the first (and so far only) function call.
356
443
  // Mixed calls are rejected by core.ts; no need to stream their args.
357
444
  if (String(event.item.name) === "final_answer" && functionCallCount === 1) {
358
- finalAnswerDetected = true;
359
- callbacks.onClearText?.();
445
+ answerStreamer.activate();
360
446
  }
361
447
  }
362
448
  break;
@@ -366,11 +452,9 @@ async function streamResponsesApi(client, createParams, callbacks, signal) {
366
452
  currentToolCall.arguments += event.delta;
367
453
  // Feed final_answer argument deltas to the parser for progressive
368
454
  // streaming, but only when it appears to be the sole function call.
369
- if (finalAnswerDetected && currentToolCall.name === "final_answer"
455
+ if (answerStreamer.detected && currentToolCall.name === "final_answer"
370
456
  && functionCallCount === 1) {
371
- const text = answerParser.process(String(event.delta));
372
- if (text)
373
- callbacks.onTextChunk(text);
457
+ answerStreamer.processDelta(String(event.delta));
374
458
  }
375
459
  }
376
460
  break;
@@ -410,6 +494,6 @@ async function streamResponsesApi(client, createParams, callbacks, signal) {
410
494
  toolCalls,
411
495
  outputItems,
412
496
  usage,
413
- finalAnswerStreamed: answerParser.active,
497
+ finalAnswerStreamed: answerStreamer.streamed,
414
498
  };
415
499
  }