@openclaw-cloud/agent-controller 1.0.0 → 2.1.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 (55) hide show
  1. package/bin/agent-controller.js +11 -2
  2. package/dist/api.d.ts +4 -0
  3. package/dist/api.js +8 -0
  4. package/dist/api.js.map +1 -1
  5. package/dist/commands/channel-server.d.ts +17 -0
  6. package/dist/commands/channel-server.js +71 -0
  7. package/dist/commands/channel-server.js.map +1 -0
  8. package/dist/config-file.js +6 -0
  9. package/dist/config-file.js.map +1 -1
  10. package/dist/config.d.ts +10 -0
  11. package/dist/config.js +20 -0
  12. package/dist/config.js.map +1 -1
  13. package/dist/connection.d.ts +2 -0
  14. package/dist/connection.js +2 -0
  15. package/dist/connection.js.map +1 -1
  16. package/dist/handlers/board-handler.js +56 -20
  17. package/dist/handlers/board-handler.js.map +1 -1
  18. package/dist/handlers/chat.d.ts +17 -0
  19. package/dist/handlers/chat.js +93 -0
  20. package/dist/handlers/chat.js.map +1 -1
  21. package/dist/handlers/memory.d.ts +7 -0
  22. package/dist/handlers/memory.js +41 -0
  23. package/dist/handlers/memory.js.map +1 -0
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.js +73 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/memory-mcp-server.d.ts +8 -0
  28. package/dist/memory-mcp-server.js +291 -0
  29. package/dist/memory-mcp-server.js.map +1 -0
  30. package/dist/providers/claude-code/channel-server.d.ts +60 -0
  31. package/dist/providers/claude-code/channel-server.js +155 -0
  32. package/dist/providers/claude-code/channel-server.js.map +1 -0
  33. package/dist/providers/claude-code/index.d.ts +68 -0
  34. package/dist/providers/claude-code/index.js +276 -0
  35. package/dist/providers/claude-code/index.js.map +1 -0
  36. package/dist/providers/claude-code/login-flow.d.ts +26 -0
  37. package/dist/providers/claude-code/login-flow.js +129 -0
  38. package/dist/providers/claude-code/login-flow.js.map +1 -0
  39. package/dist/providers/claude-code/settings-writer.d.ts +29 -0
  40. package/dist/providers/claude-code/settings-writer.js +79 -0
  41. package/dist/providers/claude-code/settings-writer.js.map +1 -0
  42. package/dist/providers/claude-code/socket-bridge.d.ts +88 -0
  43. package/dist/providers/claude-code/socket-bridge.js +302 -0
  44. package/dist/providers/claude-code/socket-bridge.js.map +1 -0
  45. package/dist/providers/claude-code/spawn-claude.d.ts +44 -0
  46. package/dist/providers/claude-code/spawn-claude.js +108 -0
  47. package/dist/providers/claude-code/spawn-claude.js.map +1 -0
  48. package/dist/providers/index.d.ts +3 -1
  49. package/dist/providers/index.js +12 -0
  50. package/dist/providers/index.js.map +1 -1
  51. package/dist/types.d.ts +1 -1
  52. package/dist/utils/knowledge-graph.d.ts +66 -0
  53. package/dist/utils/knowledge-graph.js +163 -0
  54. package/dist/utils/knowledge-graph.js.map +1 -0
  55. package/package.json +1 -1
@@ -0,0 +1,302 @@
1
+ import net from 'node:net';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import os from 'node:os';
5
+ import { EventEmitter } from 'node:events';
6
+ import { logCollector } from '../../connection.js';
7
+ import { toErrorMessage } from '../../utils/response.js';
8
+ /**
9
+ * Unix-domain-socket bridge between the main agent-controller process and the
10
+ * channel-server worker that holds Claude Code's stdio MCP channel.
11
+ *
12
+ * Protocol: newline-delimited JSON. Ops:
13
+ * { op: "hello", pid } — worker → main
14
+ * { op: "reply", content, meta } — worker → main (Claude reply tool)
15
+ * { op: "edit", content, meta } — worker → main (Claude edit_message tool)
16
+ * { op: "push", content, meta } — main → worker (inbound chat msg)
17
+ *
18
+ * Liberal parsing: malformed lines are logged + dropped (never crash the link).
19
+ */
20
+ export const DEFAULT_SOCKET_PATH = path.join(os.homedir(), '.openclaw', 'claude-channel.sock');
21
+ /**
22
+ * Server side — lives in the main agent-controller process.
23
+ *
24
+ * Accepts exactly one worker connection (extras are rejected + closed). Emits
25
+ * reply/edit ops; callers invoke `push(content, meta)` to emit an inbound
26
+ * chat message to the worker.
27
+ */
28
+ export class ChannelBridgeServer extends EventEmitter {
29
+ opts;
30
+ server = null;
31
+ active = null;
32
+ buf = '';
33
+ constructor(opts = {}) {
34
+ super();
35
+ this.opts = opts;
36
+ // Default no-op 'error' listener so re-emitting a server error never turns
37
+ // into an unhandled-exception process crash if no external listener is
38
+ // registered. External callers can still attach their own listener.
39
+ this.on('error', () => {
40
+ /* no-op */
41
+ });
42
+ }
43
+ get socketPath() {
44
+ return this.opts.socketPath ?? DEFAULT_SOCKET_PATH;
45
+ }
46
+ async listen() {
47
+ const p = this.socketPath;
48
+ await fs.promises.mkdir(path.dirname(p), { recursive: true });
49
+ // Best-effort: remove stale socket file from a previous crash.
50
+ try {
51
+ await fs.promises.unlink(p);
52
+ }
53
+ catch {
54
+ /* not present */
55
+ }
56
+ return new Promise((resolve, reject) => {
57
+ const srv = net.createServer((sock) => this.handleConnection(sock));
58
+ // One-shot rejector so listen() failures surface to the awaiter; detach
59
+ // after successful bind so it doesn't leak into runtime errors.
60
+ const onListenError = (err) => {
61
+ srv.removeListener('error', onRuntimeError);
62
+ reject(err);
63
+ };
64
+ const onRuntimeError = (err) => {
65
+ logCollector?.push('channel_bridge_error', 'error', `bridge server error: ${err.message}`);
66
+ this.emit('error', err);
67
+ };
68
+ srv.once('error', onListenError);
69
+ srv.on('error', onRuntimeError);
70
+ srv.listen(p, () => {
71
+ srv.removeListener('error', onListenError);
72
+ this.server = srv;
73
+ logCollector?.push('channel_bridge_listening', 'info', `channel bridge on ${p}`);
74
+ resolve();
75
+ });
76
+ });
77
+ }
78
+ handleConnection(sock) {
79
+ if (this.active) {
80
+ // Reject extras — we expect exactly one worker.
81
+ logCollector?.push('channel_bridge_reject_extra', 'warn', 'extra worker connection rejected — one worker already active');
82
+ try {
83
+ sock.end();
84
+ }
85
+ catch {
86
+ /* ignore */
87
+ }
88
+ return;
89
+ }
90
+ this.active = sock;
91
+ sock.setEncoding('utf8');
92
+ sock.on('data', (chunk) => this.ingest(String(chunk)));
93
+ sock.on('close', () => {
94
+ this.active = null;
95
+ this.buf = '';
96
+ logCollector?.push('channel_bridge_closed', 'info', 'worker connection closed');
97
+ this.emit('close');
98
+ });
99
+ sock.on('error', (err) => {
100
+ logCollector?.push('channel_bridge_sock_error', 'warn', `worker socket error: ${err.message}`);
101
+ });
102
+ }
103
+ ingest(chunk) {
104
+ this.buf += chunk;
105
+ let idx;
106
+ while ((idx = this.buf.indexOf('\n')) >= 0) {
107
+ const line = this.buf.slice(0, idx).trim();
108
+ this.buf = this.buf.slice(idx + 1);
109
+ if (!line)
110
+ continue;
111
+ let msg = null;
112
+ try {
113
+ msg = JSON.parse(line);
114
+ }
115
+ catch {
116
+ logCollector?.push('channel_bridge_bad_line', 'warn', 'dropped malformed bridge line');
117
+ continue;
118
+ }
119
+ this.dispatch(msg);
120
+ }
121
+ }
122
+ dispatch(msg) {
123
+ if (!msg || typeof msg !== 'object' || !msg.op)
124
+ return;
125
+ switch (msg.op) {
126
+ case 'hello': {
127
+ const pid = typeof msg.pid === 'number' ? msg.pid : null;
128
+ logCollector?.push('channel_bridge_hello', 'info', `worker hello pid=${pid ?? 'unknown'}`);
129
+ this.emit('connect', pid);
130
+ break;
131
+ }
132
+ case 'reply': {
133
+ const m = msg;
134
+ const content = typeof m.content === 'string' ? m.content : '';
135
+ const meta = (m.meta ?? {});
136
+ this.emit('reply', content, meta, 'reply');
137
+ break;
138
+ }
139
+ case 'edit': {
140
+ const m = msg;
141
+ const content = typeof m.content === 'string' ? m.content : '';
142
+ const meta = (m.meta ?? {});
143
+ this.emit('reply', content, meta, 'edit_message');
144
+ break;
145
+ }
146
+ default:
147
+ logCollector?.push('channel_bridge_unknown_op', 'warn', `unknown op: ${String(msg.op)}`);
148
+ }
149
+ }
150
+ /** Push an inbound chat message to the worker (→ Claude MCP notification). */
151
+ push(content, meta) {
152
+ this.write({ op: 'push', content, meta });
153
+ }
154
+ write(op) {
155
+ if (!this.active) {
156
+ logCollector?.push('channel_bridge_no_worker', 'warn', `dropped op ${op.op} — no worker`);
157
+ return;
158
+ }
159
+ try {
160
+ this.active.write(JSON.stringify(op) + '\n');
161
+ }
162
+ catch (err) {
163
+ logCollector?.push('channel_bridge_write_error', 'warn', `write ${op.op} failed: ${toErrorMessage(err)}`);
164
+ }
165
+ }
166
+ async close() {
167
+ try {
168
+ this.active?.end();
169
+ }
170
+ catch {
171
+ /* ignore */
172
+ }
173
+ this.active = null;
174
+ if (!this.server)
175
+ return;
176
+ const srv = this.server;
177
+ this.server = null;
178
+ await new Promise((resolve) => srv.close(() => resolve()));
179
+ try {
180
+ await fs.promises.unlink(this.socketPath);
181
+ }
182
+ catch {
183
+ /* ignore */
184
+ }
185
+ }
186
+ isConnected() {
187
+ return !!this.active;
188
+ }
189
+ }
190
+ /**
191
+ * Client side — lives in the channel-server worker subprocess.
192
+ *
193
+ * Connects to the bridge socket with retry/backoff (up to 30s). Parses
194
+ * incoming `push` ops and forwards outgoing `reply`/`edit` ops.
195
+ */
196
+ export class ChannelBridgeClient extends EventEmitter {
197
+ opts;
198
+ sock = null;
199
+ buf = '';
200
+ closed = false;
201
+ constructor(opts = {}) {
202
+ super();
203
+ this.opts = opts;
204
+ }
205
+ get socketPath() {
206
+ return this.opts.socketPath ?? DEFAULT_SOCKET_PATH;
207
+ }
208
+ async connect() {
209
+ const maxWait = this.opts.maxWaitMs ?? 30_000;
210
+ const started = Date.now();
211
+ let attempt = 0;
212
+ // Retry with capped exponential backoff while the main process comes up.
213
+ while (true) {
214
+ try {
215
+ await this.connectOnce();
216
+ return;
217
+ }
218
+ catch (err) {
219
+ if (Date.now() - started > maxWait) {
220
+ throw err instanceof Error ? err : new Error(String(err));
221
+ }
222
+ attempt += 1;
223
+ const delay = Math.min(2000, 100 * 2 ** Math.min(attempt, 6));
224
+ await new Promise((r) => setTimeout(r, delay));
225
+ }
226
+ }
227
+ }
228
+ connectOnce() {
229
+ return new Promise((resolve, reject) => {
230
+ const s = net.createConnection({ path: this.socketPath });
231
+ const onError = (err) => {
232
+ s.removeAllListeners();
233
+ reject(err);
234
+ };
235
+ s.once('error', onError);
236
+ s.once('connect', () => {
237
+ s.removeListener('error', onError);
238
+ s.setEncoding('utf8');
239
+ this.sock = s;
240
+ s.on('data', (chunk) => this.ingest(String(chunk)));
241
+ s.on('close', () => {
242
+ this.sock = null;
243
+ this.buf = '';
244
+ if (!this.closed)
245
+ this.emit('close');
246
+ });
247
+ s.on('error', (err) => this.emit('error', err));
248
+ resolve();
249
+ });
250
+ });
251
+ }
252
+ sendHello(pid) {
253
+ this.write({ op: 'hello', pid });
254
+ }
255
+ sendReply(content, meta, tool) {
256
+ this.write({ op: tool === 'edit_message' ? 'edit' : 'reply', content, meta });
257
+ }
258
+ ingest(chunk) {
259
+ this.buf += chunk;
260
+ let idx;
261
+ while ((idx = this.buf.indexOf('\n')) >= 0) {
262
+ const line = this.buf.slice(0, idx).trim();
263
+ this.buf = this.buf.slice(idx + 1);
264
+ if (!line)
265
+ continue;
266
+ let msg = null;
267
+ try {
268
+ msg = JSON.parse(line);
269
+ }
270
+ catch {
271
+ continue;
272
+ }
273
+ if (msg && msg.op === 'push') {
274
+ const m = msg;
275
+ const content = typeof m.content === 'string' ? m.content : '';
276
+ const meta = (m.meta ?? {});
277
+ this.emit('push', content, meta);
278
+ }
279
+ }
280
+ }
281
+ write(op) {
282
+ if (!this.sock)
283
+ return;
284
+ try {
285
+ this.sock.write(JSON.stringify(op) + '\n');
286
+ }
287
+ catch {
288
+ /* ignore — socket will emit close/error */
289
+ }
290
+ }
291
+ close() {
292
+ this.closed = true;
293
+ try {
294
+ this.sock?.end();
295
+ }
296
+ catch {
297
+ /* ignore */
298
+ }
299
+ this.sock = null;
300
+ }
301
+ }
302
+ //# sourceMappingURL=socket-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket-bridge.js","sourceRoot":"","sources":["../../../src/providers/claude-code/socket-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { spawn, type ChildProcess } from 'node:child_process';
2
+ import { EventEmitter } from 'node:events';
3
+ export interface SpawnClaudeDeps {
4
+ /** Working directory for the claude process (workspace). */
5
+ workspaceDir: string;
6
+ /** Channels plugin flag argument (defaults to `plugin:agent-controller`). */
7
+ channelsFlag?: string;
8
+ /** Extra CLI flags to append. */
9
+ extraArgs?: string[];
10
+ /** Environment variables to merge on top of process.env. */
11
+ env?: Record<string, string | undefined>;
12
+ /** Allow `--dangerously-load-development-channels` (default true). */
13
+ danglyDev?: boolean;
14
+ /** Override spawn (tests). */
15
+ spawnFn?: typeof spawn;
16
+ /** Max retries on crash (default 5). */
17
+ maxRetries?: number;
18
+ }
19
+ /**
20
+ * Supervise a `claude` subprocess. Emits:
21
+ * - `stderr` (Buffer)
22
+ * - `stdout` (Buffer)
23
+ * - `spawn` (ChildProcess)
24
+ * - `exit` (code|null, signal|null)
25
+ * - `gaveup` — too many crashes, no more restarts.
26
+ *
27
+ * Restart on crash with capped exponential backoff.
28
+ */
29
+ export declare class ClaudeProcess extends EventEmitter {
30
+ private readonly deps;
31
+ private child;
32
+ private retries;
33
+ private shuttingDown;
34
+ private lastSpawnAt;
35
+ constructor(deps: SpawnClaudeDeps);
36
+ start(): void;
37
+ private spawnOnce;
38
+ private buildArgs;
39
+ /** Send SIGINT to interrupt the current turn. */
40
+ interrupt(): void;
41
+ stop(): void;
42
+ isRunning(): boolean;
43
+ getChild(): ChildProcess | null;
44
+ }
@@ -0,0 +1,108 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { EventEmitter } from 'node:events';
3
+ import { logCollector } from '../../connection.js';
4
+ /**
5
+ * Supervise a `claude` subprocess. Emits:
6
+ * - `stderr` (Buffer)
7
+ * - `stdout` (Buffer)
8
+ * - `spawn` (ChildProcess)
9
+ * - `exit` (code|null, signal|null)
10
+ * - `gaveup` — too many crashes, no more restarts.
11
+ *
12
+ * Restart on crash with capped exponential backoff.
13
+ */
14
+ export class ClaudeProcess extends EventEmitter {
15
+ deps;
16
+ child = null;
17
+ retries = 0;
18
+ shuttingDown = false;
19
+ lastSpawnAt = 0;
20
+ constructor(deps) {
21
+ super();
22
+ this.deps = deps;
23
+ }
24
+ start() {
25
+ if (this.child)
26
+ return;
27
+ this.shuttingDown = false;
28
+ this.spawnOnce();
29
+ }
30
+ spawnOnce() {
31
+ const spawnFn = this.deps.spawnFn ?? spawn;
32
+ const args = this.buildArgs();
33
+ this.lastSpawnAt = Date.now();
34
+ const child = spawnFn('claude', args, {
35
+ cwd: this.deps.workspaceDir,
36
+ env: { ...process.env, ...(this.deps.env ?? {}) },
37
+ stdio: ['pipe', 'pipe', 'pipe'],
38
+ });
39
+ this.child = child;
40
+ child.stdout?.on('data', (data) => this.emit('stdout', data));
41
+ child.stderr?.on('data', (data) => {
42
+ this.emit('stderr', data);
43
+ logCollector?.push('claude_stderr', 'debug', data.toString().slice(0, 500));
44
+ });
45
+ child.on('error', (err) => {
46
+ logCollector?.push('claude_spawn_error', 'error', err.message);
47
+ this.emit('error', err);
48
+ });
49
+ child.on('exit', (code, signal) => {
50
+ this.emit('exit', code, signal);
51
+ this.child = null;
52
+ if (this.shuttingDown)
53
+ return;
54
+ // Reset retries on a healthy long run
55
+ if (Date.now() - this.lastSpawnAt > 60_000)
56
+ this.retries = 0;
57
+ const maxRetries = this.deps.maxRetries ?? 5;
58
+ // Semantics: maxRetries is the number of RESTART attempts allowed.
59
+ // Increment first, then check — so maxRetries=5 yields exactly 5 restarts
60
+ // (retry counts 1..5) before giving up on the 6th crash.
61
+ this.retries += 1;
62
+ if (this.retries > maxRetries) {
63
+ logCollector?.push('claude_giveup', 'error', `claude crashed too many times (${this.retries - 1}/${maxRetries}), giving up`);
64
+ this.emit('gaveup');
65
+ return;
66
+ }
67
+ const backoff = Math.min(30_000, 500 * 2 ** (this.retries - 1));
68
+ logCollector?.push('claude_restart_scheduled', 'warn', `claude exited code=${code ?? 'null'} sig=${signal ?? 'null'}; retry ${this.retries}/${maxRetries} in ${backoff}ms`);
69
+ setTimeout(() => {
70
+ if (!this.shuttingDown)
71
+ this.spawnOnce();
72
+ }, backoff).unref();
73
+ });
74
+ this.emit('spawn', child);
75
+ logCollector?.push('claude_spawned', 'info', `claude spawned (pid=${child.pid})`);
76
+ }
77
+ buildArgs() {
78
+ const args = ['--channels', this.deps.channelsFlag ?? 'plugin:agent-controller'];
79
+ if (this.deps.danglyDev !== false)
80
+ args.push('--dangerously-load-development-channels');
81
+ if (this.deps.extraArgs)
82
+ args.push(...this.deps.extraArgs);
83
+ return args;
84
+ }
85
+ /** Send SIGINT to interrupt the current turn. */
86
+ interrupt() {
87
+ this.child?.kill('SIGINT');
88
+ }
89
+ stop() {
90
+ this.shuttingDown = true;
91
+ if (this.child) {
92
+ try {
93
+ this.child.kill('SIGTERM');
94
+ }
95
+ catch {
96
+ /* ignore */
97
+ }
98
+ }
99
+ this.child = null;
100
+ }
101
+ isRunning() {
102
+ return !!this.child && this.child.exitCode === null;
103
+ }
104
+ getChild() {
105
+ return this.child;
106
+ }
107
+ }
108
+ //# sourceMappingURL=spawn-claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn-claude.js","sourceRoot":"","sources":["../../../src/providers/claude-code/spawn-claude.ts"],"names":[],"mappings":"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"}
@@ -4,8 +4,10 @@ export type { ChatSession, ChatMessage, ChatAttachment, ChatContentBlock, IChatP
4
4
  export { OpenclawProvider } from './openclaw/index.js';
5
5
  export { MockProvider } from './mock/index.js';
6
6
  export type { MockAction } from './mock/index.js';
7
+ export { ClaudeCodeProvider } from './claude-code/index.js';
8
+ export type { ClaudeCodeProviderConfig } from './claude-code/index.js';
7
9
  export declare function configureProvider(provider: IAgentProvider): void;
8
10
  export declare function getProvider(): IAgentProvider | null;
9
11
  /** For testing only — reset the global provider */
10
12
  export declare function resetProvider(): void;
11
- export declare function createProvider(type: string, config: Record<string, string>): IAgentProvider;
13
+ export declare function createProvider(type: string, config: Record<string, string | undefined>): IAgentProvider;
@@ -1,7 +1,9 @@
1
1
  import { OpenclawProvider } from './openclaw/index.js';
2
2
  import { MockProvider } from './mock/index.js';
3
+ import { ClaudeCodeProvider } from './claude-code/index.js';
3
4
  export { OpenclawProvider } from './openclaw/index.js';
4
5
  export { MockProvider } from './mock/index.js';
6
+ export { ClaudeCodeProvider } from './claude-code/index.js';
5
7
  let _provider = null;
6
8
  export function configureProvider(provider) {
7
9
  _provider = provider;
@@ -19,6 +21,16 @@ export function createProvider(type, config) {
19
21
  return new OpenclawProvider(config.wsUrl ?? 'ws://localhost:18789', config.wsToken ?? '');
20
22
  case 'mock':
21
23
  return new MockProvider();
24
+ case 'claude-code':
25
+ return new ClaudeCodeProvider({
26
+ workspaceDir: config.workspaceDir ?? '/home/ubuntu/.claude/workspace',
27
+ anthropicApiKey: config.anthropicApiKey,
28
+ anthropicAuthToken: config.anthropicAuthToken,
29
+ claudeCodeOauthToken: config.claudeCodeOauthToken,
30
+ backendUrl: config.backendUrl,
31
+ agentToken: config.agentToken,
32
+ claudeMdContent: config.claudeMdContent,
33
+ });
22
34
  default:
23
35
  throw new Error(`Unknown provider type: ${type}`);
24
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAU/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG/C,IAAI,SAAS,GAA0B,IAAI,CAAC;AAE5C,MAAM,UAAU,iBAAiB,CAAC,QAAwB;IACxD,SAAS,GAAG,QAAQ,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,aAAa;IAC3B,SAAS,GAAG,IAAI,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,MAA8B;IACzE,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,UAAU;YACb,OAAO,IAAI,gBAAgB,CAAC,MAAM,CAAC,KAAK,IAAI,sBAAsB,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QAC5F,KAAK,MAAM;YACT,OAAO,IAAI,YAAY,EAAE,CAAC;QAC5B;YACE,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAU5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAG5D,IAAI,SAAS,GAA0B,IAAI,CAAC;AAE5C,MAAM,UAAU,iBAAiB,CAAC,QAAwB;IACxD,SAAS,GAAG,QAAQ,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,aAAa;IAC3B,SAAS,GAAG,IAAI,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,IAAY,EACZ,MAA0C;IAE1C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,UAAU;YACb,OAAO,IAAI,gBAAgB,CAAC,MAAM,CAAC,KAAK,IAAI,sBAAsB,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QAC5F,KAAK,MAAM;YACT,OAAO,IAAI,YAAY,EAAE,CAAC;QAC5B,KAAK,aAAa;YAChB,OAAO,IAAI,kBAAkB,CAAC;gBAC5B,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,gCAAgC;gBACrE,eAAe,EAAE,MAAM,CAAC,eAAe;gBACvC,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;gBAC7C,oBAAoB,EAAE,MAAM,CAAC,oBAAoB;gBACjD,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,eAAe,EAAE,MAAM,CAAC,eAAe;aACxC,CAAC,CAAC;QACL;YACE,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;AACH,CAAC"}
package/dist/types.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * board:<workspaceId> — board events (card:entered, card:moved, etc.)
6
6
  * heartbeat:<agentId> — heartbeat publications from agent
7
7
  */
8
- export type CommandType = 'diagnostics' | 'restart' | 'deploy' | 'pair' | 'stop' | 'backup' | 'chat_list_sessions' | 'chat_history' | 'chat_send' | 'package_install' | 'file_write' | 'file_delete' | 'onboarding_complete' | 'knowledge_sync' | 'self_update' | 'telegram_webhook' | 'update_skills' | 'update_config';
8
+ export type CommandType = 'diagnostics' | 'restart' | 'deploy' | 'pair' | 'stop' | 'backup' | 'chat_list_sessions' | 'chat_history' | 'chat_send' | 'package_install' | 'file_write' | 'file_delete' | 'onboarding_complete' | 'knowledge_sync' | 'self_update' | 'telegram_webhook' | 'update_skills' | 'update_config' | 'memory_create_entities' | 'memory_create_relations' | 'memory_add_observations' | 'memory_delete_entities' | 'memory_delete_observations' | 'memory_delete_relations' | 'memory_read_graph' | 'memory_search_nodes' | 'memory_open_nodes';
9
9
  export interface AgentCommand {
10
10
  id: string;
11
11
  type: CommandType;
@@ -0,0 +1,66 @@
1
+ import { EventEmitter } from 'node:events';
2
+ export interface Entity {
3
+ name: string;
4
+ entityType: string;
5
+ observations: string[];
6
+ }
7
+ export interface Relation {
8
+ from: string;
9
+ to: string;
10
+ relationType: string;
11
+ }
12
+ export interface Graph {
13
+ entities: Entity[];
14
+ relations: Relation[];
15
+ }
16
+ export interface ObservationInput {
17
+ entityName: string;
18
+ contents: string[];
19
+ }
20
+ export interface ObservationResult {
21
+ entityName: string;
22
+ addedObservations: string[];
23
+ }
24
+ export interface ObservationDeletion {
25
+ entityName: string;
26
+ observations: string[];
27
+ }
28
+ export type MemoryDeltaAction = 'entities_created' | 'relations_created' | 'observations_added' | 'entities_deleted' | 'observations_deleted' | 'relations_deleted';
29
+ export interface MemoryDelta {
30
+ action: MemoryDeltaAction;
31
+ data: unknown;
32
+ }
33
+ /**
34
+ * In-memory knowledge graph with atomic persistence.
35
+ *
36
+ * Ported from @modelcontextprotocol/server-memory with key differences:
37
+ * - Graph stays in memory (this.graph), loaded once at init()
38
+ * - All mutations operate on this.graph, then call persist()
39
+ * - persist() uses atomic write: write to .tmp file, then fs.rename()
40
+ * - Extends EventEmitter, emits 'delta' event after each mutation
41
+ * - Query methods (readGraph, searchNodes, openNodes) are synchronous
42
+ */
43
+ export declare class KnowledgeGraphManager extends EventEmitter {
44
+ private memoryFilePath;
45
+ private graph;
46
+ private persistQueue;
47
+ constructor(memoryFilePath: string);
48
+ /** Load graph from disk into memory. Call once at startup. */
49
+ init(): Promise<void>;
50
+ private readGraphFromDisk;
51
+ /** Serialized persist: enqueues writes so concurrent calls never interleave. */
52
+ private persist;
53
+ /** Atomic persist: write to .tmp, then fs.rename(). */
54
+ private doPersist;
55
+ /** Emit delta event after mutation (for sync to backend). */
56
+ private emitDelta;
57
+ createEntities(entities: Entity[]): Promise<Entity[]>;
58
+ createRelations(relations: Relation[]): Promise<Relation[]>;
59
+ addObservations(observations: ObservationInput[]): Promise<ObservationResult[]>;
60
+ deleteEntities(names: string[]): Promise<void>;
61
+ deleteObservations(deletions: ObservationDeletion[]): Promise<void>;
62
+ deleteRelations(relations: Relation[]): Promise<void>;
63
+ readGraph(): Graph;
64
+ searchNodes(query: string): Graph;
65
+ openNodes(names: string[]): Graph;
66
+ }