@next-open-ai/openbot 0.1.1

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 (139) hide show
  1. package/README.md +212 -0
  2. package/dist/agent/agent-dir.d.ts +14 -0
  3. package/dist/agent/agent-dir.js +75 -0
  4. package/dist/agent/agent-manager.d.ts +61 -0
  5. package/dist/agent/agent-manager.js +257 -0
  6. package/dist/agent/config-manager.d.ts +25 -0
  7. package/dist/agent/config-manager.js +84 -0
  8. package/dist/agent/desktop-config.d.ts +15 -0
  9. package/dist/agent/desktop-config.js +91 -0
  10. package/dist/agent/run.d.ts +26 -0
  11. package/dist/agent/run.js +65 -0
  12. package/dist/agent/skills.d.ts +20 -0
  13. package/dist/agent/skills.js +86 -0
  14. package/dist/cli.d.ts +2 -0
  15. package/dist/cli.js +168 -0
  16. package/dist/gateway/backend-url.d.ts +2 -0
  17. package/dist/gateway/backend-url.js +11 -0
  18. package/dist/gateway/clients.d.ts +5 -0
  19. package/dist/gateway/clients.js +4 -0
  20. package/dist/gateway/connection-handler.d.ts +6 -0
  21. package/dist/gateway/connection-handler.js +48 -0
  22. package/dist/gateway/desktop-config.d.ts +7 -0
  23. package/dist/gateway/desktop-config.js +25 -0
  24. package/dist/gateway/index.d.ts +3 -0
  25. package/dist/gateway/index.js +2 -0
  26. package/dist/gateway/message-handler.d.ts +5 -0
  27. package/dist/gateway/message-handler.js +65 -0
  28. package/dist/gateway/methods/agent-cancel.d.ts +10 -0
  29. package/dist/gateway/methods/agent-cancel.js +17 -0
  30. package/dist/gateway/methods/agent-chat.d.ts +8 -0
  31. package/dist/gateway/methods/agent-chat.js +194 -0
  32. package/dist/gateway/methods/connect.d.ts +8 -0
  33. package/dist/gateway/methods/connect.js +15 -0
  34. package/dist/gateway/methods/install-skill-from-path.d.ts +13 -0
  35. package/dist/gateway/methods/install-skill-from-path.js +48 -0
  36. package/dist/gateway/methods/run-scheduled-task.d.ts +13 -0
  37. package/dist/gateway/methods/run-scheduled-task.js +164 -0
  38. package/dist/gateway/server.d.ts +10 -0
  39. package/dist/gateway/server.js +268 -0
  40. package/dist/gateway/types.d.ts +76 -0
  41. package/dist/gateway/types.js +1 -0
  42. package/dist/gateway/utils.d.ts +22 -0
  43. package/dist/gateway/utils.js +67 -0
  44. package/dist/index.d.ts +3 -0
  45. package/dist/index.js +3 -0
  46. package/dist/memory/build-summary.d.ts +6 -0
  47. package/dist/memory/build-summary.js +27 -0
  48. package/dist/memory/compaction-extension.d.ts +6 -0
  49. package/dist/memory/compaction-extension.js +23 -0
  50. package/dist/memory/embedding.d.ts +10 -0
  51. package/dist/memory/embedding.js +22 -0
  52. package/dist/memory/index.d.ts +29 -0
  53. package/dist/memory/index.js +66 -0
  54. package/dist/memory/types.d.ts +16 -0
  55. package/dist/memory/types.js +1 -0
  56. package/dist/memory/vector-store.d.ts +15 -0
  57. package/dist/memory/vector-store.js +65 -0
  58. package/dist/server/agent-config/agent-config.controller.d.ts +30 -0
  59. package/dist/server/agent-config/agent-config.controller.js +83 -0
  60. package/dist/server/agent-config/agent-config.module.d.ts +2 -0
  61. package/dist/server/agent-config/agent-config.module.js +19 -0
  62. package/dist/server/agent-config/agent-config.service.d.ts +34 -0
  63. package/dist/server/agent-config/agent-config.service.js +171 -0
  64. package/dist/server/agents/agents.controller.d.ts +41 -0
  65. package/dist/server/agents/agents.controller.js +120 -0
  66. package/dist/server/agents/agents.gateway.d.ts +21 -0
  67. package/dist/server/agents/agents.gateway.js +103 -0
  68. package/dist/server/agents/agents.module.d.ts +2 -0
  69. package/dist/server/agents/agents.module.js +20 -0
  70. package/dist/server/agents/agents.service.d.ts +63 -0
  71. package/dist/server/agents/agents.service.js +167 -0
  72. package/dist/server/app.module.d.ts +2 -0
  73. package/dist/server/app.module.js +36 -0
  74. package/dist/server/auth/auth.controller.d.ts +20 -0
  75. package/dist/server/auth/auth.controller.js +64 -0
  76. package/dist/server/auth/auth.module.d.ts +2 -0
  77. package/dist/server/auth/auth.module.js +19 -0
  78. package/dist/server/config/config.controller.d.ts +51 -0
  79. package/dist/server/config/config.controller.js +81 -0
  80. package/dist/server/config/config.module.d.ts +2 -0
  81. package/dist/server/config/config.module.js +19 -0
  82. package/dist/server/config/config.service.d.ts +34 -0
  83. package/dist/server/config/config.service.js +91 -0
  84. package/dist/server/database/database.module.d.ts +2 -0
  85. package/dist/server/database/database.module.js +18 -0
  86. package/dist/server/database/database.service.d.ts +11 -0
  87. package/dist/server/database/database.service.js +137 -0
  88. package/dist/server/main.d.ts +1 -0
  89. package/dist/server/main.js +18 -0
  90. package/dist/server/skills/skills.controller.d.ts +63 -0
  91. package/dist/server/skills/skills.controller.js +194 -0
  92. package/dist/server/skills/skills.module.d.ts +2 -0
  93. package/dist/server/skills/skills.module.js +22 -0
  94. package/dist/server/skills/skills.service.d.ts +63 -0
  95. package/dist/server/skills/skills.service.js +324 -0
  96. package/dist/server/tasks/tasks.controller.d.ts +52 -0
  97. package/dist/server/tasks/tasks.controller.js +163 -0
  98. package/dist/server/tasks/tasks.module.d.ts +2 -0
  99. package/dist/server/tasks/tasks.module.js +22 -0
  100. package/dist/server/tasks/tasks.service.d.ts +84 -0
  101. package/dist/server/tasks/tasks.service.js +313 -0
  102. package/dist/server/usage/usage.controller.d.ts +12 -0
  103. package/dist/server/usage/usage.controller.js +46 -0
  104. package/dist/server/usage/usage.module.d.ts +2 -0
  105. package/dist/server/usage/usage.module.js +19 -0
  106. package/dist/server/usage/usage.service.d.ts +21 -0
  107. package/dist/server/usage/usage.service.js +55 -0
  108. package/dist/server/users/users.controller.d.ts +35 -0
  109. package/dist/server/users/users.controller.js +69 -0
  110. package/dist/server/users/users.module.d.ts +2 -0
  111. package/dist/server/users/users.module.js +19 -0
  112. package/dist/server/users/users.service.d.ts +39 -0
  113. package/dist/server/users/users.service.js +140 -0
  114. package/dist/server/workspace/workspace.controller.d.ts +24 -0
  115. package/dist/server/workspace/workspace.controller.js +132 -0
  116. package/dist/server/workspace/workspace.module.d.ts +2 -0
  117. package/dist/server/workspace/workspace.module.js +21 -0
  118. package/dist/server/workspace/workspace.service.d.ts +25 -0
  119. package/dist/server/workspace/workspace.service.js +103 -0
  120. package/dist/tools/browser-tool.d.ts +10 -0
  121. package/dist/tools/browser-tool.js +362 -0
  122. package/dist/tools/index.d.ts +3 -0
  123. package/dist/tools/index.js +3 -0
  124. package/dist/tools/install-skill-tool.d.ts +9 -0
  125. package/dist/tools/install-skill-tool.js +77 -0
  126. package/dist/tools/save-experience-tool.d.ts +5 -0
  127. package/dist/tools/save-experience-tool.js +54 -0
  128. package/package.json +80 -0
  129. package/skills/agent-browser/SKILL.md +207 -0
  130. package/skills/agent-browser/references/authentication.md +202 -0
  131. package/skills/agent-browser/references/commands.md +259 -0
  132. package/skills/agent-browser/references/proxy-support.md +188 -0
  133. package/skills/agent-browser/references/session-management.md +193 -0
  134. package/skills/agent-browser/references/snapshot-refs.md +194 -0
  135. package/skills/agent-browser/references/video-recording.md +173 -0
  136. package/skills/agent-browser/templates/authenticated-session.sh +97 -0
  137. package/skills/agent-browser/templates/capture-workflow.sh +69 -0
  138. package/skills/agent-browser/templates/form-automation.sh +62 -0
  139. package/skills/find-skills/SKILL.md +140 -0
@@ -0,0 +1,268 @@
1
+ /**
2
+ * Avoid MaxListenersExceededWarning when Browser Tool is used repeatedly.
3
+ * Playwright/agent-browser attach abort listeners to the same AbortSignal per action;
4
+ * Node's default EventTarget maxListeners is 10.
5
+ */
6
+ const Et = globalThis.EventTarget;
7
+ if (Et?.prototype?.addEventListener && Et.prototype.setMaxListeners) {
8
+ const add = Et.prototype.addEventListener;
9
+ Et.prototype.addEventListener = function (type, listener, options) {
10
+ if (type === "abort" && typeof this.setMaxListeners === "function") {
11
+ this.setMaxListeners(32);
12
+ }
13
+ return add.call(this, type, listener, options);
14
+ };
15
+ }
16
+ import { WebSocketServer } from "ws";
17
+ import { createServer, request as httpRequest } from "http";
18
+ import { handleConnection } from "./connection-handler.js";
19
+ import { readFile, stat } from "fs/promises";
20
+ import { join, extname } from "path";
21
+ import { existsSync } from "fs";
22
+ import { spawn } from "child_process";
23
+ import { createServer as createNetServer } from "net";
24
+ import { handleRunScheduledTask } from "./methods/run-scheduled-task.js";
25
+ import { handleInstallSkillFromPath } from "./methods/install-skill-from-path.js";
26
+ import { setBackendBaseUrl } from "./backend-url.js";
27
+ /**
28
+ * Find an available port starting from startPort
29
+ */
30
+ async function findAvailablePort(startPort) {
31
+ let port = startPort;
32
+ while (true) {
33
+ try {
34
+ await new Promise((resolve, reject) => {
35
+ const server = createNetServer();
36
+ server.once("error", (err) => {
37
+ if (err.code === "EADDRINUSE") {
38
+ resolve(); // Port taken, try next
39
+ }
40
+ else {
41
+ reject(err);
42
+ }
43
+ });
44
+ server.once("listening", () => {
45
+ server.close(() => resolve()); // Port available
46
+ });
47
+ server.listen(port);
48
+ });
49
+ // If we get here and the server listened successfully (then closed), checking if it was actually available logic needs care.
50
+ // Actually the above logic is slightly flawed for "resolve on error".
51
+ // Let's refine:
52
+ // If listen succeeds -> port is free. return it.
53
+ // If EADDRINUSE -> port busy. loop continue.
54
+ const isAvailable = await new Promise((resolve) => {
55
+ const server = createNetServer();
56
+ server.once("error", () => resolve(false));
57
+ server.once("listening", () => {
58
+ server.close(() => resolve(true));
59
+ });
60
+ server.listen(port);
61
+ });
62
+ if (isAvailable)
63
+ return port;
64
+ port++;
65
+ }
66
+ catch (e) {
67
+ port++;
68
+ }
69
+ }
70
+ }
71
+ /**
72
+ * MIME types for static files
73
+ */
74
+ const MIME_TYPES = {
75
+ ".html": "text/html",
76
+ ".js": "text/javascript",
77
+ ".css": "text/css",
78
+ ".json": "application/json",
79
+ ".png": "image/png",
80
+ ".jpg": "image/jpeg",
81
+ ".gif": "image/gif",
82
+ ".svg": "image/svg+xml",
83
+ ".ico": "image/x-icon",
84
+ ".woff": "font/woff",
85
+ ".woff2": "font/woff2",
86
+ ".ttf": "font/ttf",
87
+ ".eot": "application/vnd.ms-fontobject",
88
+ };
89
+ /**
90
+ * Start WebSocket gateway server
91
+ */
92
+ export async function startGatewayServer(port = 3000) {
93
+ console.log(`Starting gateway server on port ${port}...`);
94
+ // 1. Find available port for Desktop Server
95
+ const backendPort = await findAvailablePort(3001);
96
+ console.log(`Found available port for Desktop Server: ${backendPort}`);
97
+ setBackendBaseUrl(`http://localhost:${backendPort}`);
98
+ // 2. Start Desktop Server
99
+ let backendProcess = null;
100
+ const serverPath = join(process.cwd(), "dist/server/main.js");
101
+ if (existsSync(serverPath)) {
102
+ console.log(`Spawning Desktop Server at ${serverPath}...`);
103
+ backendProcess = spawn("node", [serverPath], {
104
+ cwd: process.cwd(),
105
+ env: { ...process.env, PORT: backendPort.toString() },
106
+ stdio: ["ignore", "pipe", "pipe"], // Pipe stdout/stderr to capture logs
107
+ });
108
+ backendProcess.stdout?.on("data", (data) => {
109
+ const str = data.toString().trim();
110
+ if (str)
111
+ console.log(`[Desktop Server] ${str}`);
112
+ });
113
+ backendProcess.stderr?.on("data", (data) => {
114
+ const str = data.toString().trim();
115
+ if (str)
116
+ console.error(`[Desktop Server Error] ${str}`);
117
+ });
118
+ backendProcess.on("exit", (code) => {
119
+ if (code !== 0 && code !== null) {
120
+ console.error(`Desktop Server exited with code ${code}`);
121
+ }
122
+ });
123
+ }
124
+ else {
125
+ console.warn("⚠️ Desktop Server build not found. Skipping auto-start.");
126
+ console.warn(" Run 'npm run desktop:build' to build the server.");
127
+ }
128
+ // Create HTTP server
129
+ const httpServer = createServer(async (req, res) => {
130
+ // Simple health check endpoint
131
+ if (req.url === "/health") {
132
+ res.writeHead(200, { "Content-Type": "application/json" });
133
+ res.end(JSON.stringify({ status: "ok", timestamp: Date.now() }));
134
+ return;
135
+ }
136
+ // Scheduled task: run agent and POST assistant message back to Nest
137
+ const pathname = req.url?.split("?")[0] || "";
138
+ if (req.method === "POST" && pathname === "/run-scheduled-task") {
139
+ await handleRunScheduledTask(req, res);
140
+ return;
141
+ }
142
+ // 本地技能目录安装:在 Gateway 层直接处理,不依赖 Nest,保证桌面端稳定可用
143
+ if (req.method === "POST" && pathname === "/server-api/skills/install-from-path") {
144
+ const body = await new Promise((resolve, reject) => {
145
+ const chunks = [];
146
+ req.on("data", (chunk) => chunks.push(chunk));
147
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
148
+ req.on("error", reject);
149
+ });
150
+ try {
151
+ const parsed = JSON.parse(body || "{}");
152
+ const result = await handleInstallSkillFromPath({
153
+ path: parsed.path ?? "",
154
+ scope: parsed.scope === "workspace" ? "workspace" : "global",
155
+ workspace: parsed.workspace,
156
+ });
157
+ res.writeHead(200, { "Content-Type": "application/json" });
158
+ res.end(JSON.stringify(result));
159
+ }
160
+ catch (err) {
161
+ const message = err instanceof Error ? err.message : String(err);
162
+ const code = message.includes("required") || message.includes("不存在") || message.includes("SKILL.md") || message.includes("目录名") ? 400 : 500;
163
+ res.writeHead(code, { "Content-Type": "application/json" });
164
+ res.end(JSON.stringify({ success: false, message }));
165
+ }
166
+ return;
167
+ }
168
+ // Proxy API requests to Backend (prefixed with /server-api)
169
+ if (req.url && req.url.startsWith("/server-api")) {
170
+ const options = {
171
+ hostname: "localhost",
172
+ port: backendPort, // Use discovered port
173
+ path: req.url,
174
+ method: req.method,
175
+ headers: req.headers,
176
+ };
177
+ const proxyReq = httpRequest(options, (proxyRes) => {
178
+ res.writeHead(proxyRes.statusCode || 500, proxyRes.headers);
179
+ proxyRes.pipe(res, { end: true });
180
+ });
181
+ proxyReq.on("error", (err) => {
182
+ console.error(`Proxy error to :${backendPort}`, err);
183
+ if (!res.headersSent) {
184
+ res.writeHead(502, { "Content-Type": "application/json" });
185
+ res.end(JSON.stringify({ error: "Bad Gateway", message: "Failed to connect to backend server" }));
186
+ }
187
+ });
188
+ req.pipe(proxyReq, { end: true });
189
+ return;
190
+ }
191
+ // Serve static files
192
+ try {
193
+ const staticDir = join(process.cwd(), "desktop/renderer/dist");
194
+ // Normalize URL to remove query parameters and ensuring it starts with /
195
+ const urlPath = req.url?.split("?")[0] || "/";
196
+ // Determine file path
197
+ let filePath = join(staticDir, urlPath === "/" ? "index.html" : urlPath);
198
+ // Check if file exists
199
+ try {
200
+ const stats = await stat(filePath);
201
+ if (stats.isDirectory()) {
202
+ filePath = join(filePath, "index.html");
203
+ await stat(filePath); // Check if index.html exists
204
+ }
205
+ }
206
+ catch {
207
+ // File not found
208
+ // SPA Fallback: serve index.html for non-API requests accepting HTML
209
+ if (req.headers.accept?.includes("text/html") && req.method === "GET") {
210
+ filePath = join(staticDir, "index.html");
211
+ }
212
+ else {
213
+ res.writeHead(404, { "Content-Type": "text/plain" });
214
+ res.end("Not Found");
215
+ return;
216
+ }
217
+ }
218
+ // Read and serve file
219
+ const content = await readFile(filePath);
220
+ const ext = extname(filePath).toLowerCase();
221
+ const contentType = MIME_TYPES[ext] || "application/octet-stream";
222
+ res.writeHead(200, { "Content-Type": contentType });
223
+ res.end(content);
224
+ }
225
+ catch (error) {
226
+ console.error("Static file error:", error);
227
+ res.writeHead(500, { "Content-Type": "text/plain" });
228
+ res.end("Internal Server Error");
229
+ }
230
+ });
231
+ // Create WebSocket server
232
+ const wss = new WebSocketServer({ server: httpServer });
233
+ // Handle new connections
234
+ wss.on("connection", (ws, req) => {
235
+ handleConnection(ws, req);
236
+ });
237
+ // Start listening
238
+ await new Promise((resolve) => {
239
+ httpServer.listen(port, () => {
240
+ console.log(`✅ Gateway server listening on ws://localhost:${port}`);
241
+ console.log(` Health check: http://localhost:${port}/health`);
242
+ resolve();
243
+ });
244
+ });
245
+ // Cleanup function
246
+ const close = async () => {
247
+ console.log("Closing gateway server...");
248
+ // Stop Desktop Server
249
+ if (backendProcess) {
250
+ console.log("Stopping Desktop Server...");
251
+ backendProcess.kill();
252
+ }
253
+ // Close all WebSocket connections
254
+ wss.clients.forEach((client) => {
255
+ client.close();
256
+ });
257
+ // Close WebSocket server
258
+ await new Promise((resolve) => {
259
+ wss.close(() => resolve());
260
+ });
261
+ // Close HTTP server
262
+ await new Promise((resolve) => {
263
+ httpServer.close(() => resolve());
264
+ });
265
+ console.log("Gateway server closed");
266
+ };
267
+ return { httpServer, wss, close };
268
+ }
@@ -0,0 +1,76 @@
1
+ import type { WebSocket } from "ws";
2
+ /**
3
+ * Gateway client connection
4
+ */
5
+ export interface GatewayClient {
6
+ id: string;
7
+ ws: WebSocket;
8
+ authenticated: boolean;
9
+ sessionId?: string;
10
+ connectedAt: number;
11
+ }
12
+ /**
13
+ * Gateway message types
14
+ */
15
+ export type GatewayMessageType = "request" | "response" | "event";
16
+ /**
17
+ * Base gateway message
18
+ */
19
+ export interface GatewayMessage {
20
+ type: GatewayMessageType;
21
+ id?: string;
22
+ method?: string;
23
+ params?: any;
24
+ result?: any;
25
+ error?: {
26
+ message: string;
27
+ code?: string;
28
+ };
29
+ event?: string;
30
+ payload?: any;
31
+ }
32
+ /**
33
+ * Request message
34
+ */
35
+ export interface GatewayRequest extends GatewayMessage {
36
+ type: "request";
37
+ id: string;
38
+ method: string;
39
+ params?: any;
40
+ }
41
+ /**
42
+ * Response message
43
+ */
44
+ export interface GatewayResponse extends GatewayMessage {
45
+ type: "response";
46
+ id: string;
47
+ result?: any;
48
+ error?: {
49
+ message: string;
50
+ code?: string;
51
+ };
52
+ }
53
+ /**
54
+ * Event message
55
+ */
56
+ export interface GatewayEvent extends GatewayMessage {
57
+ type: "event";
58
+ event: string;
59
+ payload?: any;
60
+ }
61
+ /**
62
+ * Connect params
63
+ */
64
+ export interface ConnectParams {
65
+ sessionId?: string;
66
+ nonce?: string;
67
+ }
68
+ /**
69
+ * Agent chat params
70
+ */
71
+ export interface AgentChatParams {
72
+ message: string;
73
+ sessionId?: string;
74
+ /** 对话/安装目标:具体 agentId,或 "global"|"all" 表示全局;用于 install_skill 等隔离 */
75
+ targetAgentId?: string;
76
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,22 @@
1
+ import type { WebSocket } from "ws";
2
+ import type { GatewayMessage } from "./types.js";
3
+ /**
4
+ * Send JSON message to WebSocket client
5
+ */
6
+ export declare function send(ws: WebSocket, message: GatewayMessage): void;
7
+ /**
8
+ * Parse incoming WebSocket message
9
+ */
10
+ export declare function parseMessage(data: Buffer | string): GatewayMessage | null;
11
+ /**
12
+ * Create error response
13
+ */
14
+ export declare function createErrorResponse(id: string, message: string, code?: string): GatewayMessage;
15
+ /**
16
+ * Create success response
17
+ */
18
+ export declare function createSuccessResponse(id: string, result: any): GatewayMessage;
19
+ /**
20
+ * Create event message
21
+ */
22
+ export declare function createEvent(event: string, payload: any): GatewayMessage;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Send JSON message to WebSocket client
3
+ */
4
+ export function send(ws, message) {
5
+ try {
6
+ ws.send(JSON.stringify(message));
7
+ }
8
+ catch (error) {
9
+ console.error("Failed to send message:", error);
10
+ }
11
+ }
12
+ /**
13
+ * Parse incoming WebSocket message
14
+ */
15
+ export function parseMessage(data) {
16
+ try {
17
+ const text = typeof data === "string" ? data : data.toString();
18
+ const obj = JSON.parse(text);
19
+ // If type is missing, try to infer it
20
+ if (!obj.type) {
21
+ if (obj.method) {
22
+ obj.type = "request";
23
+ }
24
+ else if (obj.event) {
25
+ obj.type = "event";
26
+ }
27
+ else if (obj.result !== undefined || obj.error !== undefined) {
28
+ obj.type = "response";
29
+ }
30
+ }
31
+ return obj;
32
+ }
33
+ catch (error) {
34
+ console.error("Failed to parse message:", error);
35
+ return null;
36
+ }
37
+ }
38
+ /**
39
+ * Create error response
40
+ */
41
+ export function createErrorResponse(id, message, code) {
42
+ return {
43
+ type: "response",
44
+ id,
45
+ error: { message, code },
46
+ };
47
+ }
48
+ /**
49
+ * Create success response
50
+ */
51
+ export function createSuccessResponse(id, result) {
52
+ return {
53
+ type: "response",
54
+ id,
55
+ result,
56
+ };
57
+ }
58
+ /**
59
+ * Create event message
60
+ */
61
+ export function createEvent(event, payload) {
62
+ return {
63
+ type: "event",
64
+ event,
65
+ payload,
66
+ };
67
+ }
@@ -0,0 +1,3 @@
1
+ export { loadSkillsFromDir, loadSkillsFromPaths, formatSkillsForPrompt, type Skill, type LoadSkillsFromDirOptions, } from "./agent/skills.js";
2
+ export { run, type RunOptions, type RunResult } from "./agent/run.js";
3
+ export { getOpenbotAgentDir, ensureDefaultAgentDir } from "./agent/agent-dir.js";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { loadSkillsFromDir, loadSkillsFromPaths, formatSkillsForPrompt, } from "./agent/skills.js";
2
+ export { run } from "./agent/run.js";
3
+ export { getOpenbotAgentDir, ensureDefaultAgentDir } from "./agent/agent-dir.js";
@@ -0,0 +1,6 @@
1
+ import { type SessionEntry } from "@mariozechner/pi-coding-agent";
2
+ /**
3
+ * 从 session entries 中提取可用于存入向量库的摘要文本:
4
+ * 优先使用最新 compaction 的 summary,否则用最近若干条消息拼成短文。
5
+ */
6
+ export declare function buildSessionSummaryFromEntries(entries: SessionEntry[]): string | null;
@@ -0,0 +1,27 @@
1
+ import { getLatestCompactionEntry, } from "@mariozechner/pi-coding-agent";
2
+ import { convertToLlm } from "@mariozechner/pi-coding-agent";
3
+ /**
4
+ * 从 session entries 中提取可用于存入向量库的摘要文本:
5
+ * 优先使用最新 compaction 的 summary,否则用最近若干条消息拼成短文。
6
+ */
7
+ export function buildSessionSummaryFromEntries(entries) {
8
+ const compaction = getLatestCompactionEntry(entries);
9
+ if (compaction?.summary) {
10
+ return compaction.summary;
11
+ }
12
+ const messageEntries = entries.filter((e) => e.type === "message");
13
+ if (messageEntries.length === 0)
14
+ return null;
15
+ const recent = messageEntries.slice(-20);
16
+ const messages = recent.map((e) => e.message);
17
+ const parts = [];
18
+ for (const msg of convertToLlm(messages)) {
19
+ const role = msg.role === "user" ? "User" : "Assistant";
20
+ const content = typeof msg.content === "string"
21
+ ? msg.content
22
+ : msg.content?.map((c) => c.text ?? "").join(" ") ?? "";
23
+ if (content.trim())
24
+ parts.push(`${role}: ${content.trim()}`);
25
+ }
26
+ return parts.length > 0 ? parts.join("\n") : null;
27
+ }
@@ -0,0 +1,6 @@
1
+ import type { ExtensionFactory } from "@mariozechner/pi-coding-agent";
2
+ /**
3
+ * 创建用于在 session_compact 事件时把 compaction summary 写入向量库的 extension factory。
4
+ * 在 AgentSession 发生 compaction(自动或手动)后触发,无需等到关闭会话。
5
+ */
6
+ export declare function createCompactionMemoryExtensionFactory(sessionId: string): ExtensionFactory;
@@ -0,0 +1,23 @@
1
+ import { addMemory } from "./index.js";
2
+ /**
3
+ * 创建用于在 session_compact 事件时把 compaction summary 写入向量库的 extension factory。
4
+ * 在 AgentSession 发生 compaction(自动或手动)后触发,无需等到关闭会话。
5
+ */
6
+ export function createCompactionMemoryExtensionFactory(sessionId) {
7
+ return (pi) => {
8
+ pi.on("session_compact", async (event) => {
9
+ const summary = event.compactionEntry?.summary?.trim();
10
+ if (!summary)
11
+ return;
12
+ try {
13
+ await addMemory(summary, {
14
+ infotype: "compaction",
15
+ sessionId,
16
+ });
17
+ }
18
+ catch (_) {
19
+ // 写入失败不打断会话
20
+ }
21
+ });
22
+ };
23
+ }
@@ -0,0 +1,10 @@
1
+ export declare function getEmbedder(): Promise<(input: string, opts?: {
2
+ pooling?: string;
3
+ normalize?: boolean;
4
+ }) => Promise<{
5
+ data: Float32Array;
6
+ }>>;
7
+ /**
8
+ * 对单条文本做 embedding,mean pooling + L2 归一化
9
+ */
10
+ export declare function embed(text: string): Promise<number[]>;
@@ -0,0 +1,22 @@
1
+ /// <reference path="./vendor.d.ts" />
2
+ import { pipeline } from "@xenova/transformers";
3
+ /** 多语言小模型,中英文表现较好,适合语义检索 */
4
+ const MODEL = "Xenova/paraphrase-multilingual-MiniLM-L12-v2";
5
+ let embedder = null;
6
+ export async function getEmbedder() {
7
+ if (!embedder) {
8
+ embedder = await pipeline("feature-extraction", MODEL, { quantized: true });
9
+ }
10
+ return embedder;
11
+ }
12
+ /**
13
+ * 对单条文本做 embedding,mean pooling + L2 归一化
14
+ */
15
+ export async function embed(text) {
16
+ const ext = await getEmbedder();
17
+ const out = await ext(text, {
18
+ pooling: "mean",
19
+ normalize: true,
20
+ });
21
+ return Array.from(out.data);
22
+ }
@@ -0,0 +1,29 @@
1
+ import type { InfoType, MemorySearchResult } from "./types.js";
2
+ export { buildSessionSummaryFromEntries } from "./build-summary.js";
3
+ export type { InfoType, MemoryMetadata, MemorySearchResult } from "./types.js";
4
+ export declare function initMemory(): Promise<void>;
5
+ /**
6
+ * 将一条文本写入向量库
7
+ * @param text 内容
8
+ * @param metadata.infotype 经验总结(experience) 或 compaction 摘要(compaction)
9
+ * @param metadata.sessionId 会话 id
10
+ */
11
+ export declare function addMemory(text: string, metadata: {
12
+ infotype: InfoType;
13
+ sessionId: string;
14
+ }): Promise<string>;
15
+ export interface SearchMemoryOptions {
16
+ topK?: number;
17
+ infotype?: import("./types.js").InfoType;
18
+ sessionId?: string;
19
+ }
20
+ /**
21
+ * 按语义搜索记忆,可按 infotype、sessionId 过滤
22
+ */
23
+ export declare function searchMemory(query: string, topK?: number, options?: SearchMemoryOptions): Promise<MemorySearchResult[]>;
24
+ /** 拉取 3 条经验,格式化为「经验内容」文本(用于拼入用户消息) */
25
+ export declare function getExperienceContextForUserMessage(): Promise<string>;
26
+ /**
27
+ * 拉取 1 条 compaction 摘要,用于拼入 system prompt(新建 AgentSession 时调用,可选 sessionId)
28
+ */
29
+ export declare function getCompactionContextForSystemPrompt(sessionId?: string): Promise<string>;
@@ -0,0 +1,66 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { embed } from "./embedding.js";
3
+ import { addToStore, queryStore } from "./vector-store.js";
4
+ export { buildSessionSummaryFromEntries } from "./build-summary.js";
5
+ let initialized = false;
6
+ export async function initMemory() {
7
+ if (initialized)
8
+ return;
9
+ const { getChroma } = await import("./vector-store.js");
10
+ await getChroma();
11
+ initialized = true;
12
+ }
13
+ /**
14
+ * 将一条文本写入向量库
15
+ * @param text 内容
16
+ * @param metadata.infotype 经验总结(experience) 或 compaction 摘要(compaction)
17
+ * @param metadata.sessionId 会话 id
18
+ */
19
+ export async function addMemory(text, metadata) {
20
+ const id = randomUUID();
21
+ const vec = await embed(text);
22
+ const meta = {
23
+ ...metadata,
24
+ createdAt: new Date().toISOString(),
25
+ };
26
+ await addToStore(id, vec, text, meta);
27
+ return id;
28
+ }
29
+ /**
30
+ * 按语义搜索记忆,可按 infotype、sessionId 过滤
31
+ */
32
+ export async function searchMemory(query, topK = 10, options) {
33
+ const opts = typeof options === "object" && options != null ? options : {};
34
+ const k = opts.topK ?? topK;
35
+ const filter = opts.infotype != null || opts.sessionId != null
36
+ ? { infotype: opts.infotype, sessionId: opts.sessionId }
37
+ : undefined;
38
+ const vec = await embed(query);
39
+ const rows = await queryStore(vec, k, filter);
40
+ return rows.map((r) => ({
41
+ document: r.document,
42
+ metadata: r.metadata,
43
+ distance: r.distance,
44
+ }));
45
+ }
46
+ /** 拉取 3 条经验,格式化为「经验内容」文本(用于拼入用户消息) */
47
+ export async function getExperienceContextForUserMessage() {
48
+ const results = await searchMemory("经验 总结 技巧 方法", 3, { infotype: "experience" });
49
+ if (results.length === 0)
50
+ return "";
51
+ const lines = results.map((r, i) => `${i + 1}. ${(r.document || "").trim()}`).filter(Boolean);
52
+ if (lines.length === 0)
53
+ return "";
54
+ return "以下为相关经验:\n\n" + lines.join("\n\n");
55
+ }
56
+ /**
57
+ * 拉取 1 条 compaction 摘要,用于拼入 system prompt(新建 AgentSession 时调用,可选 sessionId)
58
+ */
59
+ export async function getCompactionContextForSystemPrompt(sessionId) {
60
+ const filter = sessionId != null ? { infotype: "compaction", sessionId } : { infotype: "compaction" };
61
+ const results = await searchMemory("对话 摘要 历史 上下文", 1, { ...filter, topK: 1 });
62
+ const doc = results[0]?.document?.trim();
63
+ if (!doc)
64
+ return "";
65
+ return "## 历史对话摘要\n\n" + doc;
66
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 长记忆 metadata 中的信息类型
3
+ * - experience: 对话结束时 Agent 调用 save_experience 工具存入的经验总结
4
+ * - compaction: 关闭 session 时从 compaction 取出的会话摘要
5
+ */
6
+ export type InfoType = "experience" | "compaction";
7
+ export interface MemoryMetadata {
8
+ infotype: InfoType;
9
+ sessionId: string;
10
+ createdAt: string;
11
+ }
12
+ export interface MemorySearchResult {
13
+ document: string;
14
+ metadata: MemoryMetadata;
15
+ distance?: number;
16
+ }
@@ -0,0 +1 @@
1
+ export {};