@xiaoxiamimengfb/my-opencode-mem 2.12.0

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 (152) hide show
  1. package/README.md +155 -0
  2. package/dist/config.d.ts +58 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/config.js +411 -0
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +427 -0
  8. package/dist/plugin.d.ts +5 -0
  9. package/dist/plugin.d.ts.map +1 -0
  10. package/dist/plugin.js +4 -0
  11. package/dist/services/ai/ai-provider-factory.d.ts +8 -0
  12. package/dist/services/ai/ai-provider-factory.d.ts.map +1 -0
  13. package/dist/services/ai/ai-provider-factory.js +28 -0
  14. package/dist/services/ai/opencode-provider.d.ts +30 -0
  15. package/dist/services/ai/opencode-provider.d.ts.map +1 -0
  16. package/dist/services/ai/opencode-provider.js +332 -0
  17. package/dist/services/ai/provider-config.d.ts +17 -0
  18. package/dist/services/ai/provider-config.d.ts.map +1 -0
  19. package/dist/services/ai/provider-config.js +14 -0
  20. package/dist/services/ai/providers/anthropic-messages.d.ts +12 -0
  21. package/dist/services/ai/providers/anthropic-messages.d.ts.map +1 -0
  22. package/dist/services/ai/providers/anthropic-messages.js +184 -0
  23. package/dist/services/ai/providers/base-provider.d.ts +25 -0
  24. package/dist/services/ai/providers/base-provider.d.ts.map +1 -0
  25. package/dist/services/ai/providers/base-provider.js +23 -0
  26. package/dist/services/ai/providers/google-gemini.d.ts +16 -0
  27. package/dist/services/ai/providers/google-gemini.d.ts.map +1 -0
  28. package/dist/services/ai/providers/google-gemini.js +228 -0
  29. package/dist/services/ai/providers/openai-chat-completion.d.ts +13 -0
  30. package/dist/services/ai/providers/openai-chat-completion.d.ts.map +1 -0
  31. package/dist/services/ai/providers/openai-chat-completion.js +277 -0
  32. package/dist/services/ai/providers/openai-responses.d.ts +14 -0
  33. package/dist/services/ai/providers/openai-responses.d.ts.map +1 -0
  34. package/dist/services/ai/providers/openai-responses.js +182 -0
  35. package/dist/services/ai/session/ai-session-manager.d.ts +21 -0
  36. package/dist/services/ai/session/ai-session-manager.d.ts.map +1 -0
  37. package/dist/services/ai/session/ai-session-manager.js +166 -0
  38. package/dist/services/ai/session/session-types.d.ts +43 -0
  39. package/dist/services/ai/session/session-types.d.ts.map +1 -0
  40. package/dist/services/ai/session/session-types.js +1 -0
  41. package/dist/services/ai/tools/tool-schema.d.ts +41 -0
  42. package/dist/services/ai/tools/tool-schema.d.ts.map +1 -0
  43. package/dist/services/ai/tools/tool-schema.js +24 -0
  44. package/dist/services/ai/validators/user-profile-validator.d.ts +13 -0
  45. package/dist/services/ai/validators/user-profile-validator.d.ts.map +1 -0
  46. package/dist/services/ai/validators/user-profile-validator.js +111 -0
  47. package/dist/services/api-handlers.d.ts +164 -0
  48. package/dist/services/api-handlers.d.ts.map +1 -0
  49. package/dist/services/api-handlers.js +901 -0
  50. package/dist/services/auto-capture.d.ts +3 -0
  51. package/dist/services/auto-capture.d.ts.map +1 -0
  52. package/dist/services/auto-capture.js +306 -0
  53. package/dist/services/cleanup-service.d.ts +23 -0
  54. package/dist/services/cleanup-service.d.ts.map +1 -0
  55. package/dist/services/cleanup-service.js +102 -0
  56. package/dist/services/client.d.ts +118 -0
  57. package/dist/services/client.d.ts.map +1 -0
  58. package/dist/services/client.js +251 -0
  59. package/dist/services/context.d.ts +11 -0
  60. package/dist/services/context.d.ts.map +1 -0
  61. package/dist/services/context.js +24 -0
  62. package/dist/services/deduplication-service.d.ts +30 -0
  63. package/dist/services/deduplication-service.d.ts.map +1 -0
  64. package/dist/services/deduplication-service.js +124 -0
  65. package/dist/services/embedding.d.ts +15 -0
  66. package/dist/services/embedding.d.ts.map +1 -0
  67. package/dist/services/embedding.js +106 -0
  68. package/dist/services/jsonc.d.ts +7 -0
  69. package/dist/services/jsonc.d.ts.map +1 -0
  70. package/dist/services/jsonc.js +76 -0
  71. package/dist/services/language-detector.d.ts +3 -0
  72. package/dist/services/language-detector.d.ts.map +1 -0
  73. package/dist/services/language-detector.js +16 -0
  74. package/dist/services/logger.d.ts +2 -0
  75. package/dist/services/logger.d.ts.map +1 -0
  76. package/dist/services/logger.js +51 -0
  77. package/dist/services/migration-service.d.ts +42 -0
  78. package/dist/services/migration-service.d.ts.map +1 -0
  79. package/dist/services/migration-service.js +250 -0
  80. package/dist/services/privacy.d.ts +3 -0
  81. package/dist/services/privacy.d.ts.map +1 -0
  82. package/dist/services/privacy.js +7 -0
  83. package/dist/services/secret-resolver.d.ts +2 -0
  84. package/dist/services/secret-resolver.d.ts.map +1 -0
  85. package/dist/services/secret-resolver.js +55 -0
  86. package/dist/services/sqlite/connection-manager.d.ts +13 -0
  87. package/dist/services/sqlite/connection-manager.d.ts.map +1 -0
  88. package/dist/services/sqlite/connection-manager.js +74 -0
  89. package/dist/services/sqlite/shard-manager.d.ts +23 -0
  90. package/dist/services/sqlite/shard-manager.d.ts.map +1 -0
  91. package/dist/services/sqlite/shard-manager.js +288 -0
  92. package/dist/services/sqlite/sqlite-bootstrap.d.ts +2 -0
  93. package/dist/services/sqlite/sqlite-bootstrap.d.ts.map +1 -0
  94. package/dist/services/sqlite/sqlite-bootstrap.js +8 -0
  95. package/dist/services/sqlite/types.d.ts +42 -0
  96. package/dist/services/sqlite/types.d.ts.map +1 -0
  97. package/dist/services/sqlite/types.js +1 -0
  98. package/dist/services/sqlite/vector-search.d.ts +29 -0
  99. package/dist/services/sqlite/vector-search.d.ts.map +1 -0
  100. package/dist/services/sqlite/vector-search.js +268 -0
  101. package/dist/services/tags.d.ts +24 -0
  102. package/dist/services/tags.d.ts.map +1 -0
  103. package/dist/services/tags.js +146 -0
  104. package/dist/services/user-memory-learning.d.ts +3 -0
  105. package/dist/services/user-memory-learning.d.ts.map +1 -0
  106. package/dist/services/user-memory-learning.js +231 -0
  107. package/dist/services/user-profile/profile-context.d.ts +2 -0
  108. package/dist/services/user-profile/profile-context.d.ts.map +1 -0
  109. package/dist/services/user-profile/profile-context.js +40 -0
  110. package/dist/services/user-profile/profile-utils.d.ts +3 -0
  111. package/dist/services/user-profile/profile-utils.d.ts.map +1 -0
  112. package/dist/services/user-profile/profile-utils.js +45 -0
  113. package/dist/services/user-profile/types.d.ts +46 -0
  114. package/dist/services/user-profile/types.d.ts.map +1 -0
  115. package/dist/services/user-profile/types.js +1 -0
  116. package/dist/services/user-profile/user-profile-manager.d.ts +23 -0
  117. package/dist/services/user-profile/user-profile-manager.d.ts.map +1 -0
  118. package/dist/services/user-profile/user-profile-manager.js +292 -0
  119. package/dist/services/user-prompt/user-prompt-manager.d.ts +41 -0
  120. package/dist/services/user-prompt/user-prompt-manager.d.ts.map +1 -0
  121. package/dist/services/user-prompt/user-prompt-manager.js +192 -0
  122. package/dist/services/vector-backends/backend-factory.d.ts +3 -0
  123. package/dist/services/vector-backends/backend-factory.d.ts.map +1 -0
  124. package/dist/services/vector-backends/backend-factory.js +104 -0
  125. package/dist/services/vector-backends/exact-scan-backend.d.ts +39 -0
  126. package/dist/services/vector-backends/exact-scan-backend.d.ts.map +1 -0
  127. package/dist/services/vector-backends/exact-scan-backend.js +63 -0
  128. package/dist/services/vector-backends/types.d.ts +51 -0
  129. package/dist/services/vector-backends/types.d.ts.map +1 -0
  130. package/dist/services/vector-backends/types.js +1 -0
  131. package/dist/services/vector-backends/usearch-backend.d.ts +47 -0
  132. package/dist/services/vector-backends/usearch-backend.d.ts.map +1 -0
  133. package/dist/services/vector-backends/usearch-backend.js +174 -0
  134. package/dist/services/web-server-worker.d.ts +2 -0
  135. package/dist/services/web-server-worker.d.ts.map +1 -0
  136. package/dist/services/web-server-worker.js +283 -0
  137. package/dist/services/web-server.d.ts +31 -0
  138. package/dist/services/web-server.d.ts.map +1 -0
  139. package/dist/services/web-server.js +356 -0
  140. package/dist/types/index.d.ts +19 -0
  141. package/dist/types/index.d.ts.map +1 -0
  142. package/dist/types/index.js +1 -0
  143. package/dist/web/app.d.ts +2 -0
  144. package/dist/web/app.d.ts.map +1 -0
  145. package/dist/web/app.js +1194 -0
  146. package/dist/web/favicon.ico +0 -0
  147. package/dist/web/i18n.d.ts +2 -0
  148. package/dist/web/i18n.d.ts.map +1 -0
  149. package/dist/web/i18n.js +265 -0
  150. package/dist/web/index.html +284 -0
  151. package/dist/web/styles.css +1631 -0
  152. package/package.json +71 -0
@@ -0,0 +1,228 @@
1
+ import { BaseAIProvider } from "./base-provider.js";
2
+ import { AISessionManager } from "../session/ai-session-manager.js";
3
+ import { log } from "../../logger.js";
4
+ import { UserProfileValidator } from "../validators/user-profile-validator.js";
5
+ /**
6
+ * Google Gemini Provider
7
+ * Supports Google's Gemini models (e.g. gemini-1.5-flash) via Google AI Studio API.
8
+ */
9
+ export class GoogleGeminiProvider extends BaseAIProvider {
10
+ aiSessionManager;
11
+ constructor(config, aiSessionManager) {
12
+ super(config);
13
+ this.aiSessionManager = aiSessionManager;
14
+ }
15
+ getProviderName() {
16
+ return "google-gemini";
17
+ }
18
+ supportsSession() {
19
+ return true;
20
+ }
21
+ addToolResponse(sessionId, messages, toolCallId, content) {
22
+ const sequence = this.aiSessionManager.getLastSequence(sessionId) + 1;
23
+ this.aiSessionManager.addMessage({
24
+ aiSessionId: sessionId,
25
+ sequence,
26
+ role: "tool",
27
+ content,
28
+ toolCallId,
29
+ });
30
+ // Gemini tool response format
31
+ messages.push({
32
+ role: "function",
33
+ parts: [
34
+ {
35
+ functionResponse: {
36
+ name: toolCallId.split(":")[0], // Gemini expects the name of the function
37
+ response: JSON.parse(content),
38
+ },
39
+ },
40
+ ],
41
+ });
42
+ }
43
+ async executeToolCall(systemPrompt, userPrompt, toolSchema, sessionId) {
44
+ let session = this.aiSessionManager.getSession(sessionId, "google-gemini");
45
+ if (!session) {
46
+ session = this.aiSessionManager.createSession({
47
+ provider: "google-gemini",
48
+ sessionId,
49
+ });
50
+ }
51
+ const existingMessages = this.aiSessionManager.getMessages(session.id);
52
+ const contents = [];
53
+ // System instruction is separate in Gemini API
54
+ const geminiSystemInstruction = {
55
+ parts: [{ text: systemPrompt }],
56
+ };
57
+ // Convert existing messages to Gemini format
58
+ for (const msg of existingMessages) {
59
+ if (msg.role === "system")
60
+ continue; // Skip system as it's passed separately
61
+ const role = msg.role === "assistant" ? "model" : "user";
62
+ const parts = [];
63
+ if (msg.content) {
64
+ parts.push({ text: msg.content });
65
+ }
66
+ if (msg.toolCalls) {
67
+ for (const tc of msg.toolCalls) {
68
+ parts.push({
69
+ functionCall: {
70
+ name: tc.function.name,
71
+ args: JSON.parse(tc.function.arguments),
72
+ },
73
+ });
74
+ }
75
+ }
76
+ if (msg.role === "tool") {
77
+ contents.push({
78
+ role: "function",
79
+ parts: [
80
+ {
81
+ functionResponse: {
82
+ name: (msg.toolCallId || "").split(":")[0],
83
+ response: JSON.parse(msg.content),
84
+ },
85
+ },
86
+ ],
87
+ });
88
+ continue;
89
+ }
90
+ contents.push({ role, parts });
91
+ }
92
+ if (contents.length === 0 || contents[contents.length - 1].role !== "user") {
93
+ const userSequence = this.aiSessionManager.getLastSequence(session.id) + 1;
94
+ this.aiSessionManager.addMessage({
95
+ aiSessionId: session.id,
96
+ sequence: userSequence,
97
+ role: "user",
98
+ content: userPrompt,
99
+ });
100
+ contents.push({ role: "user", parts: [{ text: userPrompt }] });
101
+ }
102
+ let iterations = 0;
103
+ const maxIterations = this.config.maxIterations ?? 5;
104
+ const iterationTimeout = this.config.iterationTimeout ?? 30000;
105
+ // Gemini API expects the tool name as a function declaration
106
+ const tools = [
107
+ {
108
+ functionDeclarations: [
109
+ {
110
+ name: toolSchema.function.name,
111
+ description: toolSchema.function.description,
112
+ parameters: toolSchema.function.parameters,
113
+ },
114
+ ],
115
+ },
116
+ ];
117
+ while (iterations < maxIterations) {
118
+ iterations++;
119
+ const controller = new AbortController();
120
+ const timeout = setTimeout(() => controller.abort(), iterationTimeout);
121
+ try {
122
+ const baseUrl = this.config.apiUrl || "https://generativelanguage.googleapis.com/v1beta";
123
+ const url = `${baseUrl}/models/${this.config.model}:generateContent?key=${this.config.apiKey}`;
124
+ const requestBody = {
125
+ contents,
126
+ systemInstruction: geminiSystemInstruction,
127
+ tools,
128
+ toolConfig: {
129
+ functionCallingConfig: {
130
+ mode: "ANY", // Force function calling
131
+ allowedFunctionNames: [toolSchema.function.name],
132
+ },
133
+ },
134
+ generationConfig: {
135
+ temperature: this.config.memoryTemperature ?? 0.3,
136
+ },
137
+ };
138
+ const response = await fetch(url, {
139
+ method: "POST",
140
+ headers: { "Content-Type": "application/json" },
141
+ body: JSON.stringify(requestBody),
142
+ signal: controller.signal,
143
+ });
144
+ clearTimeout(timeout);
145
+ if (!response.ok) {
146
+ const errorText = await response.text().catch(() => response.statusText);
147
+ log("Gemini API error", {
148
+ provider: this.getProviderName(),
149
+ model: this.config.model,
150
+ status: response.status,
151
+ error: errorText,
152
+ iteration: iterations,
153
+ });
154
+ return {
155
+ success: false,
156
+ error: `Gemini API error: ${response.status} - ${errorText}`,
157
+ iterations,
158
+ };
159
+ }
160
+ const data = (await response.json());
161
+ const candidate = data.candidates?.[0];
162
+ if (!candidate || !candidate.content) {
163
+ return { success: false, error: "Invalid Gemini API response format", iterations };
164
+ }
165
+ const modelMsg = candidate.content;
166
+ const assistantSequence = this.aiSessionManager.getLastSequence(session.id) + 1;
167
+ // Map Gemini response back to our internal message format
168
+ const assistantMsg = {
169
+ aiSessionId: session.id,
170
+ sequence: assistantSequence,
171
+ role: "assistant",
172
+ content: "",
173
+ toolCalls: [],
174
+ };
175
+ for (const part of modelMsg.parts) {
176
+ if (part.text)
177
+ assistantMsg.content += part.text;
178
+ if (part.functionCall) {
179
+ assistantMsg.toolCalls.push({
180
+ id: `${part.functionCall.name}:${Date.now()}`,
181
+ type: "function",
182
+ function: {
183
+ name: part.functionCall.name,
184
+ arguments: JSON.stringify(part.functionCall.args),
185
+ },
186
+ });
187
+ }
188
+ }
189
+ this.aiSessionManager.addMessage(assistantMsg);
190
+ contents.push(modelMsg);
191
+ if (assistantMsg.toolCalls.length > 0) {
192
+ for (const toolCall of assistantMsg.toolCalls) {
193
+ if (toolCall.function.name === toolSchema.function.name) {
194
+ try {
195
+ const parsed = JSON.parse(toolCall.function.arguments);
196
+ const result = UserProfileValidator.validate(parsed);
197
+ if (!result.valid)
198
+ throw new Error(result.errors.join(", "));
199
+ this.addToolResponse(session.id, contents, toolCall.id, JSON.stringify({ success: true }));
200
+ return { success: true, data: result.data, iterations };
201
+ }
202
+ catch (validationError) {
203
+ const errorMessage = `Validation failed: ${String(validationError)}`;
204
+ this.addToolResponse(session.id, contents, toolCall.id, JSON.stringify({ success: false, error: errorMessage }));
205
+ return { success: false, error: errorMessage, iterations };
206
+ }
207
+ }
208
+ }
209
+ }
210
+ // Retry if no tool call was made
211
+ const retryPrompt = "Please use the save_memories tool as instructed.";
212
+ const retrySequence = this.aiSessionManager.getLastSequence(session.id) + 1;
213
+ this.aiSessionManager.addMessage({
214
+ aiSessionId: session.id,
215
+ sequence: retrySequence,
216
+ role: "user",
217
+ content: retryPrompt,
218
+ });
219
+ contents.push({ role: "user", parts: [{ text: retryPrompt }] });
220
+ }
221
+ catch (error) {
222
+ clearTimeout(timeout);
223
+ return { success: false, error: String(error), iterations };
224
+ }
225
+ }
226
+ return { success: false, error: `Max iterations (${maxIterations}) reached`, iterations };
227
+ }
228
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseAIProvider, type ToolCallResult } from "./base-provider.js";
2
+ import { AISessionManager } from "../session/ai-session-manager.js";
3
+ import type { ChatCompletionTool } from "../tools/tool-schema.js";
4
+ export declare class OpenAIChatCompletionProvider extends BaseAIProvider {
5
+ private aiSessionManager;
6
+ constructor(config: any, aiSessionManager: AISessionManager);
7
+ getProviderName(): string;
8
+ supportsSession(): boolean;
9
+ private addToolResponse;
10
+ private filterIncompleteToolCallSequences;
11
+ executeToolCall(systemPrompt: string, userPrompt: string, toolSchema: ChatCompletionTool, sessionId: string): Promise<ToolCallResult>;
12
+ }
13
+ //# sourceMappingURL=openai-chat-completion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-chat-completion.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/providers/openai-chat-completion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAwB,MAAM,oBAAoB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAqBlE,qBAAa,4BAA6B,SAAQ,cAAc;IAC9D,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,gBAAgB;IAK3D,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,OAAO;IAI1B,OAAO,CAAC,eAAe;IAqBvB,OAAO,CAAC,iCAAiC;IAoCnC,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;CAmR3B"}
@@ -0,0 +1,277 @@
1
+ import { BaseAIProvider, applySafeExtraParams } from "./base-provider.js";
2
+ import { AISessionManager } from "../session/ai-session-manager.js";
3
+ import { log } from "../../logger.js";
4
+ import { UserProfileValidator } from "../validators/user-profile-validator.js";
5
+ export class OpenAIChatCompletionProvider extends BaseAIProvider {
6
+ aiSessionManager;
7
+ constructor(config, aiSessionManager) {
8
+ super(config);
9
+ this.aiSessionManager = aiSessionManager;
10
+ }
11
+ getProviderName() {
12
+ return "openai-chat";
13
+ }
14
+ supportsSession() {
15
+ return true;
16
+ }
17
+ addToolResponse(sessionId, messages, toolCallId, content) {
18
+ const sequence = this.aiSessionManager.getLastSequence(sessionId) + 1;
19
+ this.aiSessionManager.addMessage({
20
+ aiSessionId: sessionId,
21
+ sequence,
22
+ role: "tool",
23
+ content,
24
+ toolCallId,
25
+ });
26
+ messages.push({
27
+ role: "tool",
28
+ tool_call_id: toolCallId,
29
+ content,
30
+ });
31
+ }
32
+ filterIncompleteToolCallSequences(messages) {
33
+ const result = [];
34
+ let i = 0;
35
+ while (i < messages.length) {
36
+ const msg = messages[i];
37
+ if (msg.role === "assistant" && msg.toolCalls && msg.toolCalls.length > 0) {
38
+ const toolCallIds = new Set(msg.toolCalls.map((tc) => tc.id));
39
+ const toolResponses = [];
40
+ let j = i + 1;
41
+ while (j < messages.length && messages[j].role === "tool") {
42
+ if (toolCallIds.has(messages[j].toolCallId)) {
43
+ toolResponses.push(messages[j]);
44
+ toolCallIds.delete(messages[j].toolCallId);
45
+ }
46
+ j++;
47
+ }
48
+ if (toolCallIds.size === 0) {
49
+ result.push(msg);
50
+ toolResponses.forEach((tr) => result.push(tr));
51
+ i = j;
52
+ }
53
+ else {
54
+ break;
55
+ }
56
+ }
57
+ else {
58
+ result.push(msg);
59
+ i++;
60
+ }
61
+ }
62
+ return result;
63
+ }
64
+ async executeToolCall(systemPrompt, userPrompt, toolSchema, sessionId) {
65
+ let session = this.aiSessionManager.getSession(sessionId, "openai-chat");
66
+ if (!session) {
67
+ session = this.aiSessionManager.createSession({
68
+ provider: "openai-chat",
69
+ sessionId,
70
+ });
71
+ }
72
+ const existingMessages = this.aiSessionManager.getMessages(session.id);
73
+ const messages = [];
74
+ const validatedMessages = this.filterIncompleteToolCallSequences(existingMessages);
75
+ for (const msg of validatedMessages) {
76
+ const apiMsg = {
77
+ role: msg.role,
78
+ content: msg.content,
79
+ };
80
+ if (msg.toolCalls) {
81
+ apiMsg.tool_calls = msg.toolCalls;
82
+ }
83
+ if (msg.toolCallId) {
84
+ apiMsg.tool_call_id = msg.toolCallId;
85
+ }
86
+ messages.push(apiMsg);
87
+ }
88
+ if (messages.length === 0) {
89
+ const sequence = this.aiSessionManager.getLastSequence(session.id) + 1;
90
+ this.aiSessionManager.addMessage({
91
+ aiSessionId: session.id,
92
+ sequence,
93
+ role: "system",
94
+ content: systemPrompt,
95
+ });
96
+ messages.push({ role: "system", content: systemPrompt });
97
+ }
98
+ const userSequence = this.aiSessionManager.getLastSequence(session.id) + 1;
99
+ this.aiSessionManager.addMessage({
100
+ aiSessionId: session.id,
101
+ sequence: userSequence,
102
+ role: "user",
103
+ content: userPrompt,
104
+ });
105
+ messages.push({ role: "user", content: userPrompt });
106
+ let iterations = 0;
107
+ const maxIterations = this.config.maxIterations ?? 5;
108
+ const iterationTimeout = this.config.iterationTimeout ?? 30000;
109
+ while (iterations < maxIterations) {
110
+ iterations++;
111
+ const controller = new AbortController();
112
+ const timeout = setTimeout(() => controller.abort(), iterationTimeout);
113
+ try {
114
+ const requestBody = {
115
+ model: this.config.model,
116
+ messages,
117
+ tools: [toolSchema],
118
+ tool_choice: "auto",
119
+ };
120
+ if (this.config.memoryTemperature !== false) {
121
+ requestBody.temperature = this.config.memoryTemperature ?? 0.3;
122
+ }
123
+ if (this.config.extraParams) {
124
+ applySafeExtraParams(requestBody, this.config.extraParams);
125
+ }
126
+ const headers = {
127
+ "Content-Type": "application/json",
128
+ };
129
+ if (this.config.apiKey) {
130
+ headers.Authorization = `Bearer ${this.config.apiKey}`;
131
+ }
132
+ const response = await fetch(`${this.config.apiUrl}/chat/completions`, {
133
+ method: "POST",
134
+ headers,
135
+ body: JSON.stringify(requestBody),
136
+ signal: controller.signal,
137
+ });
138
+ clearTimeout(timeout);
139
+ if (!response.ok) {
140
+ const errorText = await response.text().catch(() => response.statusText);
141
+ log("OpenAI Chat Completion API error", {
142
+ provider: this.getProviderName(),
143
+ model: this.config.model,
144
+ status: response.status,
145
+ error: errorText,
146
+ iteration: iterations,
147
+ });
148
+ let errorMessage = `API error: ${response.status} - ${errorText}`;
149
+ if (response.status === 400 &&
150
+ errorText.includes("unsupported_value") &&
151
+ errorText.includes("temperature")) {
152
+ errorMessage =
153
+ 'Your model does not support the temperature parameter. Add "memoryTemperature": false to your config file to disable it.';
154
+ }
155
+ return {
156
+ success: false,
157
+ error: errorMessage,
158
+ iterations,
159
+ };
160
+ }
161
+ const data = (await response.json());
162
+ if (data.status && data.msg) {
163
+ log("API returned error in response body", {
164
+ provider: this.getProviderName(),
165
+ model: this.config.model,
166
+ status: data.status,
167
+ msg: data.msg,
168
+ });
169
+ return {
170
+ success: false,
171
+ error: `API error: ${data.status} - ${data.msg}`,
172
+ iterations,
173
+ };
174
+ }
175
+ if (!data.choices || !data.choices[0]) {
176
+ log("Invalid API response format", {
177
+ provider: this.getProviderName(),
178
+ model: this.config.model,
179
+ response: JSON.stringify(data).slice(0, 1000),
180
+ hasChoices: !!data.choices,
181
+ choicesLength: data.choices?.length,
182
+ });
183
+ return {
184
+ success: false,
185
+ error: "Invalid API response format",
186
+ iterations,
187
+ };
188
+ }
189
+ const choice = data.choices[0];
190
+ const assistantSequence = this.aiSessionManager.getLastSequence(session.id) + 1;
191
+ const assistantMsg = {
192
+ aiSessionId: session.id,
193
+ sequence: assistantSequence,
194
+ role: "assistant",
195
+ content: choice.message.content || "",
196
+ };
197
+ if (choice.message.tool_calls) {
198
+ assistantMsg.toolCalls = choice.message.tool_calls;
199
+ }
200
+ this.aiSessionManager.addMessage(assistantMsg);
201
+ messages.push(choice.message);
202
+ if (choice.message.tool_calls && choice.message.tool_calls.length > 0) {
203
+ for (const toolCall of choice.message.tool_calls) {
204
+ const toolCallId = toolCall.id;
205
+ if (toolCall.function.name === toolSchema.function.name) {
206
+ try {
207
+ const parsed = JSON.parse(toolCall.function.arguments);
208
+ const result = UserProfileValidator.validate(parsed);
209
+ if (!result.valid) {
210
+ throw new Error(result.errors.join(", "));
211
+ }
212
+ this.addToolResponse(session.id, messages, toolCallId, JSON.stringify({ success: true }));
213
+ return {
214
+ success: true,
215
+ data: result.data,
216
+ iterations,
217
+ };
218
+ }
219
+ catch (validationError) {
220
+ const errorStack = validationError instanceof Error ? validationError.stack : undefined;
221
+ log("OpenAI tool response validation failed", {
222
+ error: String(validationError),
223
+ stack: errorStack,
224
+ errorType: validationError instanceof Error
225
+ ? validationError.constructor.name
226
+ : typeof validationError,
227
+ toolName: toolSchema.function.name,
228
+ iteration: iterations,
229
+ rawArguments: toolCall.function.arguments.slice(0, 500),
230
+ });
231
+ const errorMessage = `Validation failed: ${String(validationError)}`;
232
+ this.addToolResponse(session.id, messages, toolCallId, JSON.stringify({ success: false, error: errorMessage }));
233
+ return {
234
+ success: false,
235
+ error: errorMessage,
236
+ iterations,
237
+ };
238
+ }
239
+ }
240
+ const wrongToolMessage = `Wrong tool called. Please use ${toolSchema.function.name} instead.`;
241
+ this.addToolResponse(session.id, messages, toolCallId, JSON.stringify({ success: false, error: wrongToolMessage }));
242
+ break;
243
+ }
244
+ }
245
+ const retrySequence = this.aiSessionManager.getLastSequence(session.id) + 1;
246
+ const retryPrompt = "Please use the save_memories tool to extract and save the memories from the conversation as instructed.";
247
+ this.aiSessionManager.addMessage({
248
+ aiSessionId: session.id,
249
+ sequence: retrySequence,
250
+ role: "user",
251
+ content: retryPrompt,
252
+ });
253
+ messages.push({ role: "user", content: retryPrompt });
254
+ }
255
+ catch (error) {
256
+ clearTimeout(timeout);
257
+ if (error instanceof Error && error.name === "AbortError") {
258
+ return {
259
+ success: false,
260
+ error: `API request timeout (${this.config.iterationTimeout}ms)`,
261
+ iterations,
262
+ };
263
+ }
264
+ return {
265
+ success: false,
266
+ error: String(error),
267
+ iterations,
268
+ };
269
+ }
270
+ }
271
+ return {
272
+ success: false,
273
+ error: `Max iterations (${this.config.maxIterations}) reached without tool call`,
274
+ iterations,
275
+ };
276
+ }
277
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseAIProvider, type ToolCallResult } from "./base-provider.js";
2
+ import { AISessionManager } from "../session/ai-session-manager.js";
3
+ import { type ChatCompletionTool } from "../tools/tool-schema.js";
4
+ export declare class OpenAIResponsesProvider extends BaseAIProvider {
5
+ private aiSessionManager;
6
+ constructor(config: any, aiSessionManager: AISessionManager);
7
+ getProviderName(): string;
8
+ supportsSession(): boolean;
9
+ executeToolCall(systemPrompt: string, userPrompt: string, toolSchema: ChatCompletionTool, sessionId: string): Promise<ToolCallResult>;
10
+ private extractToolCall;
11
+ private buildRetryPrompt;
12
+ private validateResponse;
13
+ }
14
+ //# sourceMappingURL=openai-responses.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-responses.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/providers/openai-responses.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAwB,MAAM,oBAAoB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAsBvF,qBAAa,uBAAwB,SAAQ,cAAc;IACzD,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,gBAAgB;IAK3D,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,OAAO;IAIpB,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;IA0H1B,OAAO,CAAC,eAAe;IA+BvB,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,gBAAgB;CAsBzB"}