@osmosis-ai/openclaw 0.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.
- package/dist/config.d.ts +18 -0
- package/dist/config.js +15 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon.d.ts +10 -0
- package/dist/daemon.js +60 -0
- package/dist/daemon.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +11 -0
- package/dist/init.js +28 -0
- package/dist/init.js.map +1 -0
- package/dist/inject.d.ts +6 -0
- package/dist/inject.js +31 -0
- package/dist/inject.js.map +1 -0
- package/dist/instrument.d.ts +10 -0
- package/dist/instrument.js +26 -0
- package/dist/instrument.js.map +1 -0
- package/dist/watcher.d.ts +48 -0
- package/dist/watcher.js +241 -0
- package/dist/watcher.js.map +1 -0
- package/package.json +27 -0
- package/src/__tests__/config.test.ts +28 -0
- package/src/__tests__/e2e.test.ts +148 -0
- package/src/__tests__/inject.test.ts +58 -0
- package/src/__tests__/instrument.test.ts +48 -0
- package/src/config.ts +33 -0
- package/src/daemon.ts +69 -0
- package/src/index.ts +8 -0
- package/src/init.ts +41 -0
- package/src/inject.ts +37 -0
- package/src/instrument.ts +32 -0
- package/src/watcher.ts +307 -0
- package/tsconfig.json +24 -0
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface OsmosisConfig {
|
|
2
|
+
/** Enable Osmosis integration (default: false) */
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
/** Path to the SQLite database (default: ~/.osmosis/atoms.db) */
|
|
5
|
+
dbPath: string;
|
|
6
|
+
/** REST API port (default: 7432) */
|
|
7
|
+
apiPort: number;
|
|
8
|
+
/** Capture tool calls automatically (default: true when enabled) */
|
|
9
|
+
captureToolCalls: boolean;
|
|
10
|
+
/** Inject knowledge context at task start (default: true when enabled) */
|
|
11
|
+
injectContext: boolean;
|
|
12
|
+
/** Mesh server URL for sync */
|
|
13
|
+
meshUrl: string;
|
|
14
|
+
/** Sync interval in ms (default: 5 min) */
|
|
15
|
+
syncInterval: number;
|
|
16
|
+
}
|
|
17
|
+
export declare const DEFAULT_CONFIG: OsmosisConfig;
|
|
18
|
+
export declare function resolveConfig(partial?: Partial<OsmosisConfig>): OsmosisConfig;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
export const DEFAULT_CONFIG = {
|
|
4
|
+
enabled: false,
|
|
5
|
+
dbPath: join(homedir(), '.osmosis', 'atoms.db'),
|
|
6
|
+
apiPort: 7432,
|
|
7
|
+
captureToolCalls: true,
|
|
8
|
+
injectContext: true,
|
|
9
|
+
meshUrl: 'https://mesh.osmosis.dev',
|
|
10
|
+
syncInterval: 5 * 60 * 1000,
|
|
11
|
+
};
|
|
12
|
+
export function resolveConfig(partial) {
|
|
13
|
+
return { ...DEFAULT_CONFIG, ...partial };
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAmBjC,MAAM,CAAC,MAAM,cAAc,GAAkB;IAC3C,OAAO,EAAE,KAAK;IACd,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,UAAU,CAAC;IAC/C,OAAO,EAAE,IAAI;IACb,gBAAgB,EAAE,IAAI;IACtB,aAAa,EAAE,IAAI;IACnB,OAAO,EAAE,0BAA0B;IACnC,YAAY,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI;CAC5B,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,OAAgC;IAC5D,OAAO,EAAE,GAAG,cAAc,EAAE,GAAG,OAAO,EAAE,CAAC;AAC3C,CAAC"}
|
package/dist/daemon.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Osmosis Daemon for OpenClaw
|
|
4
|
+
*
|
|
5
|
+
* Runs as a systemd service alongside OpenClaw. Responsibilities:
|
|
6
|
+
* 1. Watch agent session transcripts for tool calls → capture as atoms
|
|
7
|
+
* 2. Serve local REST API for agents to query knowledge
|
|
8
|
+
* 3. Auto-sync local atoms to the mesh server
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
package/dist/daemon.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Osmosis Daemon for OpenClaw
|
|
4
|
+
*
|
|
5
|
+
* Runs as a systemd service alongside OpenClaw. Responsibilities:
|
|
6
|
+
* 1. Watch agent session transcripts for tool calls → capture as atoms
|
|
7
|
+
* 2. Serve local REST API for agents to query knowledge
|
|
8
|
+
* 3. Auto-sync local atoms to the mesh server
|
|
9
|
+
*/
|
|
10
|
+
import { initOsmosis } from './init.js';
|
|
11
|
+
import { TranscriptWatcher } from './watcher.js';
|
|
12
|
+
import { resolveConfig } from './config.js';
|
|
13
|
+
const MESH_URL = process.env.OSMOSIS_MESH_URL ?? 'https://osmosis-mesh-dev.fly.dev';
|
|
14
|
+
const DB_PATH = process.env.OSMOSIS_DB_PATH ?? `${process.env.HOME ?? '/root'}/.osmosis/atoms.db`;
|
|
15
|
+
const API_PORT = parseInt(process.env.OSMOSIS_API_PORT ?? '7432', 10);
|
|
16
|
+
const OPENCLAW_DIR = process.env.OPENCLAW_DIR ?? `${process.env.HOME ?? '/root'}/.openclaw`;
|
|
17
|
+
const SYNC_INTERVAL = parseInt(process.env.OSMOSIS_SYNC_INTERVAL ?? '300000', 10); // 5 min
|
|
18
|
+
console.log('🧠 Osmosis daemon starting...');
|
|
19
|
+
console.log(` Mesh: ${MESH_URL}`);
|
|
20
|
+
console.log(` DB: ${DB_PATH}`);
|
|
21
|
+
console.log(` API: http://localhost:${API_PORT}`);
|
|
22
|
+
console.log(` OpenClaw: ${OPENCLAW_DIR}`);
|
|
23
|
+
console.log(` Sync: every ${SYNC_INTERVAL / 1000}s`);
|
|
24
|
+
// Initialize Osmosis (local store + sync server + auto-sync)
|
|
25
|
+
const handle = initOsmosis(resolveConfig({
|
|
26
|
+
enabled: true,
|
|
27
|
+
dbPath: DB_PATH,
|
|
28
|
+
apiPort: API_PORT,
|
|
29
|
+
meshUrl: MESH_URL,
|
|
30
|
+
syncInterval: SYNC_INTERVAL,
|
|
31
|
+
captureToolCalls: true,
|
|
32
|
+
injectContext: true,
|
|
33
|
+
}));
|
|
34
|
+
// Start transcript watcher
|
|
35
|
+
const watcher = new TranscriptWatcher(handle.store, {
|
|
36
|
+
openclawDir: OPENCLAW_DIR,
|
|
37
|
+
scanIntervalMs: 10_000,
|
|
38
|
+
maxAgeMs: 24 * 60 * 60 * 1000,
|
|
39
|
+
});
|
|
40
|
+
watcher.start();
|
|
41
|
+
// Status endpoint on the local API
|
|
42
|
+
const statsInterval = setInterval(() => {
|
|
43
|
+
const stats = watcher.stats;
|
|
44
|
+
const allAtoms = handle.store.getAll();
|
|
45
|
+
if (stats.capturedCount > 0 || allAtoms.length > 0) {
|
|
46
|
+
console.log(`📊 Atoms: ${allAtoms.length} local | Captured: ${stats.capturedCount} | Files: ${stats.filesWatched}`);
|
|
47
|
+
}
|
|
48
|
+
}, 60_000);
|
|
49
|
+
// Graceful shutdown
|
|
50
|
+
function shutdown() {
|
|
51
|
+
console.log('\n🧠 Osmosis daemon shutting down...');
|
|
52
|
+
clearInterval(statsInterval);
|
|
53
|
+
watcher.stop();
|
|
54
|
+
handle.stop();
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
process.on('SIGINT', shutdown);
|
|
58
|
+
process.on('SIGTERM', shutdown);
|
|
59
|
+
console.log('🧠 Osmosis daemon running. Watching for agent activity...');
|
|
60
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { instrumentToolCall } from './instrument.js';
|
|
2
|
+
export type { ToolCallFn } from './instrument.js';
|
|
3
|
+
export { getRelevantContext } from './inject.js';
|
|
4
|
+
export { resolveConfig, DEFAULT_CONFIG } from './config.js';
|
|
5
|
+
export type { OsmosisConfig } from './config.js';
|
|
6
|
+
export { initOsmosis } from './init.js';
|
|
7
|
+
export type { OsmosisHandle } from './init.js';
|
|
8
|
+
export { TranscriptWatcher } from './watcher.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { instrumentToolCall } from './instrument.js';
|
|
2
|
+
export { getRelevantContext } from './inject.js';
|
|
3
|
+
export { resolveConfig, DEFAULT_CONFIG } from './config.js';
|
|
4
|
+
export { initOsmosis } from './init.js';
|
|
5
|
+
export { TranscriptWatcher } from './watcher.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5D,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/init.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AtomStore } from '@osmosis-ai/core';
|
|
2
|
+
import type { AutoSyncHandle } from '@osmosis-ai/sync';
|
|
3
|
+
import type { Server } from 'node:http';
|
|
4
|
+
import type { OsmosisConfig } from './config.js';
|
|
5
|
+
export interface OsmosisHandle {
|
|
6
|
+
store: AtomStore;
|
|
7
|
+
server: Server;
|
|
8
|
+
autoSync: AutoSyncHandle;
|
|
9
|
+
stop(): void;
|
|
10
|
+
}
|
|
11
|
+
export declare function initOsmosis(config: OsmosisConfig): OsmosisHandle;
|
package/dist/init.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AtomStore } from '@osmosis-ai/core';
|
|
2
|
+
import { createSyncServer, startAutoSync, resolveSyncConfig } from '@osmosis-ai/sync';
|
|
3
|
+
import { mkdirSync } from 'node:fs';
|
|
4
|
+
export function initOsmosis(config) {
|
|
5
|
+
// Ensure DB directory exists
|
|
6
|
+
const dir = config.dbPath.substring(0, config.dbPath.lastIndexOf('/'));
|
|
7
|
+
if (dir)
|
|
8
|
+
mkdirSync(dir, { recursive: true });
|
|
9
|
+
const store = new AtomStore(config.dbPath);
|
|
10
|
+
const syncConfig = resolveSyncConfig({
|
|
11
|
+
meshUrl: config.meshUrl,
|
|
12
|
+
autoSync: !!config.meshUrl,
|
|
13
|
+
syncIntervalMs: config.syncInterval,
|
|
14
|
+
});
|
|
15
|
+
const server = createSyncServer(store, config.apiPort, syncConfig);
|
|
16
|
+
const autoSync = startAutoSync(store, syncConfig);
|
|
17
|
+
return {
|
|
18
|
+
store,
|
|
19
|
+
server,
|
|
20
|
+
autoSync,
|
|
21
|
+
stop() {
|
|
22
|
+
autoSync.stop();
|
|
23
|
+
server.close();
|
|
24
|
+
store.close();
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAItF,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AASpC,MAAM,UAAU,WAAW,CAAC,MAAqB;IAC/C,6BAA6B;IAC7B,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IACvE,IAAI,GAAG;QAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAE3C,MAAM,UAAU,GAAG,iBAAiB,CAAC;QACnC,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO;QAC1B,cAAc,EAAE,MAAM,CAAC,YAAY;KACpC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACnE,MAAM,QAAQ,GAAG,aAAa,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAElD,OAAO;QACL,KAAK;QACL,MAAM;QACN,QAAQ;QACR,IAAI;YACF,QAAQ,CAAC,IAAI,EAAE,CAAC;YAChB,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/inject.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type AtomStore } from '@osmosis-ai/core';
|
|
2
|
+
/**
|
|
3
|
+
* Query the local store for relevant knowledge atoms and format them
|
|
4
|
+
* as concise context tips for the agent.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getRelevantContext(taskDescription: string, store: AtomStore, limit?: number): string;
|
package/dist/inject.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { searchAtoms, getTopAtoms } from '@osmosis-ai/core';
|
|
2
|
+
/**
|
|
3
|
+
* Query the local store for relevant knowledge atoms and format them
|
|
4
|
+
* as concise context tips for the agent.
|
|
5
|
+
*/
|
|
6
|
+
export function getRelevantContext(taskDescription, store, limit = 5) {
|
|
7
|
+
// Search for atoms relevant to the task description
|
|
8
|
+
const searched = searchAtoms(store, taskDescription, limit);
|
|
9
|
+
// If search returns few results, pad with top fitness atoms
|
|
10
|
+
let atoms = searched;
|
|
11
|
+
if (atoms.length < limit) {
|
|
12
|
+
const top = getTopAtoms(store, undefined, limit - atoms.length);
|
|
13
|
+
const ids = new Set(atoms.map(a => a.id));
|
|
14
|
+
for (const a of top) {
|
|
15
|
+
if (!ids.has(a.id))
|
|
16
|
+
atoms.push(a);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (atoms.length === 0)
|
|
20
|
+
return '';
|
|
21
|
+
const lines = atoms.map(a => formatAtomTip(a));
|
|
22
|
+
return lines.join('\n');
|
|
23
|
+
}
|
|
24
|
+
function formatAtomTip(atom) {
|
|
25
|
+
const toolName = atom.tool_name;
|
|
26
|
+
const prefix = toolName ? `Tool ${toolName}` : atom.type;
|
|
27
|
+
const error = atom.error_signature;
|
|
28
|
+
const workaround = error ? ` Workaround: check error "${error}"` : '';
|
|
29
|
+
return `⚡ ${prefix}: ${atom.observation}.${workaround}`;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=inject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inject.js","sourceRoot":"","sources":["../src/inject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEhG;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,eAAuB,EACvB,KAAgB,EAChB,QAAgB,CAAC;IAEjB,oDAAoD;IACpD,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,CAAC,CAAC;IAE5D,4DAA4D;IAC5D,IAAI,KAAK,GAAoB,QAAQ,CAAC;IACtC,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;QAChE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1C,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;YACpB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,aAAa,CAAC,IAAmB;IACxC,MAAM,QAAQ,GAAI,IAAY,CAAC,SAAS,CAAC;IACzC,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IACzD,MAAM,KAAK,GAAI,IAAY,CAAC,eAAe,CAAC;IAC5C,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,6BAA6B,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACtE,OAAO,KAAK,MAAM,KAAK,IAAI,CAAC,WAAW,IAAI,UAAU,EAAE,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type AtomStore } from '@osmosis-ai/core';
|
|
2
|
+
export interface ToolCallFn {
|
|
3
|
+
(toolName: string, params: Record<string, unknown>): Promise<unknown>;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Wrap a tool execution function to automatically capture calls in Osmosis.
|
|
7
|
+
* Returns a new function with the same signature that records tool name, params,
|
|
8
|
+
* result, error, and latency.
|
|
9
|
+
*/
|
|
10
|
+
export declare function instrumentToolCall(originalFn: ToolCallFn, store: AtomStore): ToolCallFn;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { captureToolCall } from '@osmosis-ai/core';
|
|
2
|
+
/**
|
|
3
|
+
* Wrap a tool execution function to automatically capture calls in Osmosis.
|
|
4
|
+
* Returns a new function with the same signature that records tool name, params,
|
|
5
|
+
* result, error, and latency.
|
|
6
|
+
*/
|
|
7
|
+
export function instrumentToolCall(originalFn, store) {
|
|
8
|
+
return async (toolName, params) => {
|
|
9
|
+
const start = performance.now();
|
|
10
|
+
let result;
|
|
11
|
+
let error = null;
|
|
12
|
+
try {
|
|
13
|
+
result = await originalFn(toolName, params);
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
error = err instanceof Error ? err.message : String(err);
|
|
18
|
+
throw err;
|
|
19
|
+
}
|
|
20
|
+
finally {
|
|
21
|
+
const latencyMs = Math.round(performance.now() - start);
|
|
22
|
+
captureToolCall(store, toolName, params, result, error, latencyMs);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=instrument.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrument.js","sourceRoot":"","sources":["../src/instrument.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAkB,MAAM,kBAAkB,CAAC;AAMnE;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,UAAsB,EACtB,KAAgB;IAEhB,OAAO,KAAK,EAAE,QAAgB,EAAE,MAA+B,EAAoB,EAAE;QACnF,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAChC,IAAI,MAAe,CAAC;QACpB,IAAI,KAAK,GAAkB,IAAI,CAAC;QAEhC,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC5C,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzD,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;YACxD,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QACrE,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw Transcript Watcher
|
|
3
|
+
*
|
|
4
|
+
* Tails OpenClaw session JSONL files and captures tool calls as KnowledgeAtoms.
|
|
5
|
+
* This is the passive instrumentation layer — agents don't need to do anything,
|
|
6
|
+
* the watcher observes their work and captures knowledge automatically.
|
|
7
|
+
*/
|
|
8
|
+
import { AtomStore } from '@osmosis-ai/core';
|
|
9
|
+
interface WatcherConfig {
|
|
10
|
+
/** OpenClaw state directory (default: ~/.openclaw) */
|
|
11
|
+
openclawDir: string;
|
|
12
|
+
/** Agent directories to watch */
|
|
13
|
+
agentDirs: string[];
|
|
14
|
+
/** How often to scan for new sessions (ms) */
|
|
15
|
+
scanIntervalMs: number;
|
|
16
|
+
/** Ignore tool calls older than this (ms) */
|
|
17
|
+
maxAgeMs: number;
|
|
18
|
+
}
|
|
19
|
+
export declare class TranscriptWatcher {
|
|
20
|
+
private store;
|
|
21
|
+
private config;
|
|
22
|
+
private fileStates;
|
|
23
|
+
private scanTimer;
|
|
24
|
+
private pendingCalls;
|
|
25
|
+
private capturedCount;
|
|
26
|
+
constructor(store: AtomStore, config?: Partial<WatcherConfig>);
|
|
27
|
+
/** Start watching for new tool calls */
|
|
28
|
+
start(): void;
|
|
29
|
+
/** Stop watching */
|
|
30
|
+
stop(): void;
|
|
31
|
+
/** Get stats */
|
|
32
|
+
get stats(): {
|
|
33
|
+
filesWatched: number;
|
|
34
|
+
capturedCount: number;
|
|
35
|
+
pendingCalls: number;
|
|
36
|
+
};
|
|
37
|
+
/** Scan for session JSONL files */
|
|
38
|
+
private scanSessions;
|
|
39
|
+
/** Get all session directories to watch */
|
|
40
|
+
private getSessionDirs;
|
|
41
|
+
/** Process new lines from a JSONL file */
|
|
42
|
+
private processFile;
|
|
43
|
+
/** Process a single JSONL line */
|
|
44
|
+
private processLine;
|
|
45
|
+
/** Extract agent ID from file path */
|
|
46
|
+
private extractAgentId;
|
|
47
|
+
}
|
|
48
|
+
export {};
|
package/dist/watcher.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw Transcript Watcher
|
|
3
|
+
*
|
|
4
|
+
* Tails OpenClaw session JSONL files and captures tool calls as KnowledgeAtoms.
|
|
5
|
+
* This is the passive instrumentation layer — agents don't need to do anything,
|
|
6
|
+
* the watcher observes their work and captures knowledge automatically.
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
|
|
9
|
+
import { join, basename } from 'node:path';
|
|
10
|
+
import { createHash } from 'node:crypto';
|
|
11
|
+
import { captureToolCall } from '@osmosis-ai/core';
|
|
12
|
+
const DEFAULT_CONFIG = {
|
|
13
|
+
openclawDir: join(process.env.HOME ?? '/root', '.openclaw'),
|
|
14
|
+
agentDirs: [],
|
|
15
|
+
scanIntervalMs: 10_000,
|
|
16
|
+
maxAgeMs: 24 * 60 * 60 * 1000, // 24h
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Extract tool calls from a JSONL message entry.
|
|
20
|
+
* OpenClaw stores messages as JSON lines with role, content[], timestamp.
|
|
21
|
+
*/
|
|
22
|
+
function extractToolCalls(line) {
|
|
23
|
+
const calls = [];
|
|
24
|
+
try {
|
|
25
|
+
const raw = JSON.parse(line);
|
|
26
|
+
// OpenClaw JSONL format: {type: "message", message: {role, content}}
|
|
27
|
+
// or flat: {role, content}
|
|
28
|
+
const entry = raw.message ?? raw;
|
|
29
|
+
const ts = raw.timestamp ?? entry.timestamp;
|
|
30
|
+
// Tool call messages (assistant role with toolCall content)
|
|
31
|
+
if (entry.role === 'assistant' && Array.isArray(entry.content)) {
|
|
32
|
+
for (const block of entry.content) {
|
|
33
|
+
if (block.type === 'toolCall' && block.name) {
|
|
34
|
+
calls.push({
|
|
35
|
+
toolName: block.name,
|
|
36
|
+
params: typeof block.arguments === 'object' ? block.arguments : {},
|
|
37
|
+
timestamp: typeof ts === 'string' ? new Date(ts).getTime() : ts,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Tool result messages (role: "toolResult" in OpenClaw JSONL)
|
|
43
|
+
if (entry.role === 'toolResult' && entry.toolCallId) {
|
|
44
|
+
const content = Array.isArray(entry.content) ? entry.content : [];
|
|
45
|
+
const resultText = content.map((c) => c.text ?? c.data ?? '').join('');
|
|
46
|
+
const truncated = resultText.slice(0, 500);
|
|
47
|
+
const isError = entry.isError === true ||
|
|
48
|
+
(entry.details?.status === 'error') ||
|
|
49
|
+
(typeof resultText === 'string' && (resultText.includes('Error:') ||
|
|
50
|
+
resultText.includes('ENOENT') ||
|
|
51
|
+
resultText.includes('ECONNREFUSED') ||
|
|
52
|
+
resultText.includes('Command failed')));
|
|
53
|
+
calls.push({
|
|
54
|
+
toolName: entry.toolCallId,
|
|
55
|
+
params: {},
|
|
56
|
+
result: truncated,
|
|
57
|
+
error: isError ? (entry.details?.error ?? resultText.slice(0, 200)) : undefined,
|
|
58
|
+
timestamp: typeof ts === 'string' ? new Date(ts).getTime() : (entry.timestamp ?? ts),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Skip malformed lines
|
|
64
|
+
}
|
|
65
|
+
return calls;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Hash an agent identifier for privacy.
|
|
69
|
+
*/
|
|
70
|
+
function hashAgent(agentId) {
|
|
71
|
+
return createHash('sha256').update(agentId).digest('hex').slice(0, 12);
|
|
72
|
+
}
|
|
73
|
+
export class TranscriptWatcher {
|
|
74
|
+
store;
|
|
75
|
+
config;
|
|
76
|
+
fileStates = new Map();
|
|
77
|
+
scanTimer = null;
|
|
78
|
+
pendingCalls = new Map();
|
|
79
|
+
capturedCount = 0;
|
|
80
|
+
constructor(store, config) {
|
|
81
|
+
this.store = store;
|
|
82
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
83
|
+
}
|
|
84
|
+
/** Start watching for new tool calls */
|
|
85
|
+
start() {
|
|
86
|
+
// Initial scan
|
|
87
|
+
this.scanSessions();
|
|
88
|
+
// Periodic scan for new sessions
|
|
89
|
+
this.scanTimer = setInterval(() => this.scanSessions(), this.config.scanIntervalMs);
|
|
90
|
+
console.log(`🔭 Osmosis watcher started (scanning every ${this.config.scanIntervalMs / 1000}s)`);
|
|
91
|
+
}
|
|
92
|
+
/** Stop watching */
|
|
93
|
+
stop() {
|
|
94
|
+
if (this.scanTimer) {
|
|
95
|
+
clearInterval(this.scanTimer);
|
|
96
|
+
this.scanTimer = null;
|
|
97
|
+
}
|
|
98
|
+
console.log(`🔭 Osmosis watcher stopped (captured ${this.capturedCount} tool calls)`);
|
|
99
|
+
}
|
|
100
|
+
/** Get stats */
|
|
101
|
+
get stats() {
|
|
102
|
+
return {
|
|
103
|
+
filesWatched: this.fileStates.size,
|
|
104
|
+
capturedCount: this.capturedCount,
|
|
105
|
+
pendingCalls: this.pendingCalls.size,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/** Scan for session JSONL files */
|
|
109
|
+
scanSessions() {
|
|
110
|
+
const dirs = this.getSessionDirs();
|
|
111
|
+
for (const dir of dirs) {
|
|
112
|
+
if (!existsSync(dir))
|
|
113
|
+
continue;
|
|
114
|
+
try {
|
|
115
|
+
const files = readdirSync(dir).filter(f => f.endsWith('.jsonl'));
|
|
116
|
+
for (const file of files) {
|
|
117
|
+
const fullPath = join(dir, file);
|
|
118
|
+
this.processFile(fullPath);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// Skip inaccessible dirs
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Also check workspace for transcript files
|
|
126
|
+
const workspaceDir = join(this.config.openclawDir, 'workspace');
|
|
127
|
+
if (existsSync(workspaceDir)) {
|
|
128
|
+
try {
|
|
129
|
+
const files = readdirSync(workspaceDir).filter(f => f.endsWith('.jsonl'));
|
|
130
|
+
for (const file of files) {
|
|
131
|
+
this.processFile(join(workspaceDir, file));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Skip
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** Get all session directories to watch */
|
|
140
|
+
getSessionDirs() {
|
|
141
|
+
const dirs = [];
|
|
142
|
+
const agentsDir = join(this.config.openclawDir, 'agents');
|
|
143
|
+
if (existsSync(agentsDir)) {
|
|
144
|
+
try {
|
|
145
|
+
for (const agent of readdirSync(agentsDir)) {
|
|
146
|
+
const sessionsDir = join(agentsDir, agent, 'sessions');
|
|
147
|
+
if (existsSync(sessionsDir)) {
|
|
148
|
+
dirs.push(sessionsDir);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// Skip
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Add any explicitly configured dirs
|
|
157
|
+
dirs.push(...this.config.agentDirs);
|
|
158
|
+
return dirs;
|
|
159
|
+
}
|
|
160
|
+
/** Process new lines from a JSONL file */
|
|
161
|
+
processFile(filePath) {
|
|
162
|
+
try {
|
|
163
|
+
const stat = statSync(filePath);
|
|
164
|
+
const state = this.fileStates.get(filePath);
|
|
165
|
+
const currentOffset = state?.offset ?? 0;
|
|
166
|
+
// Skip if file hasn't grown
|
|
167
|
+
if (stat.size <= currentOffset)
|
|
168
|
+
return;
|
|
169
|
+
// Skip files older than maxAge (based on mtime)
|
|
170
|
+
if (Date.now() - stat.mtimeMs > this.config.maxAgeMs)
|
|
171
|
+
return;
|
|
172
|
+
// Read new content
|
|
173
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
174
|
+
const newContent = content.slice(currentOffset);
|
|
175
|
+
const lines = newContent.split('\n').filter(l => l.trim());
|
|
176
|
+
const agentId = this.extractAgentId(filePath);
|
|
177
|
+
const agentHash = hashAgent(agentId);
|
|
178
|
+
for (const line of lines) {
|
|
179
|
+
this.processLine(line, agentHash);
|
|
180
|
+
}
|
|
181
|
+
this.fileStates.set(filePath, { path: filePath, offset: stat.size });
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// Skip problematic files
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/** Process a single JSONL line */
|
|
188
|
+
processLine(line, agentHash) {
|
|
189
|
+
const calls = extractToolCalls(line);
|
|
190
|
+
for (const call of calls) {
|
|
191
|
+
// If this is a tool call (from assistant), store it pending by toolCall ID
|
|
192
|
+
if (!call.result && !call.error && call.toolName) {
|
|
193
|
+
// toolName here is the actual tool name (e.g., "exec", "read")
|
|
194
|
+
// We need to also store the toolCall ID to match results
|
|
195
|
+
// Extract IDs from the raw line
|
|
196
|
+
const raw = JSON.parse(line);
|
|
197
|
+
const entry = raw.message ?? raw;
|
|
198
|
+
if (entry.role === 'assistant' && Array.isArray(entry.content)) {
|
|
199
|
+
for (const block of entry.content) {
|
|
200
|
+
if (block.type === 'toolCall' && block.id) {
|
|
201
|
+
this.pendingCalls.set(block.id, {
|
|
202
|
+
toolName: block.name,
|
|
203
|
+
params: typeof block.arguments === 'object' ? block.arguments : {},
|
|
204
|
+
timestamp: call.timestamp,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
// If this is a tool result, match with pending call by ID
|
|
212
|
+
const callId = call.toolName; // For results, toolName holds the toolCallId
|
|
213
|
+
const pending = this.pendingCalls.get(callId);
|
|
214
|
+
if (pending) {
|
|
215
|
+
captureToolCall(this.store, pending.toolName, pending.params, call.result, call.error ?? null, pending.timestamp && call.timestamp
|
|
216
|
+
? Math.round(call.timestamp - pending.timestamp)
|
|
217
|
+
: null);
|
|
218
|
+
this.pendingCalls.delete(callId);
|
|
219
|
+
this.capturedCount++;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Expire old pending calls (>5 min)
|
|
223
|
+
const now = Date.now();
|
|
224
|
+
for (const [key, pending] of this.pendingCalls) {
|
|
225
|
+
if (pending.timestamp && now - pending.timestamp > 5 * 60 * 1000) {
|
|
226
|
+
this.pendingCalls.delete(key);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/** Extract agent ID from file path */
|
|
231
|
+
extractAgentId(filePath) {
|
|
232
|
+
// Path: ~/.openclaw/agents/{agentId}/sessions/{sessionId}.jsonl
|
|
233
|
+
const parts = filePath.split('/');
|
|
234
|
+
const agentsIdx = parts.indexOf('agents');
|
|
235
|
+
if (agentsIdx >= 0 && agentsIdx + 1 < parts.length) {
|
|
236
|
+
return parts[agentsIdx + 1];
|
|
237
|
+
}
|
|
238
|
+
return basename(filePath, '.jsonl');
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@osmosis-ai/openclaw",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "OpenClaw integration hook for Osmosis — instrument tool calls and inject knowledge context",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"osmosis-daemon": "dist/daemon.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"test:watch": "vitest"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@osmosis-ai/core": "*",
|
|
18
|
+
"@osmosis-ai/sync": "*"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"better-sqlite3": "^11.7.0",
|
|
22
|
+
"@types/better-sqlite3": "^7.6.12",
|
|
23
|
+
"typescript": "^5.7.0",
|
|
24
|
+
"vitest": "^3.0.0"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT"
|
|
27
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { resolveConfig, DEFAULT_CONFIG } from '../config.js';
|
|
3
|
+
|
|
4
|
+
describe('config', () => {
|
|
5
|
+
it('returns defaults when no overrides', () => {
|
|
6
|
+
const config = resolveConfig();
|
|
7
|
+
expect(config.enabled).toBe(false);
|
|
8
|
+
expect(config.apiPort).toBe(7432);
|
|
9
|
+
expect(config.captureToolCalls).toBe(true);
|
|
10
|
+
expect(config.injectContext).toBe(true);
|
|
11
|
+
expect(config.dbPath).toContain('.osmosis');
|
|
12
|
+
expect(config.meshUrl).toBe('https://mesh.osmosis.dev');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('merges partial overrides', () => {
|
|
16
|
+
const config = resolveConfig({ enabled: true, apiPort: 9000, meshUrl: 'http://localhost:7433' });
|
|
17
|
+
expect(config.enabled).toBe(true);
|
|
18
|
+
expect(config.apiPort).toBe(9000);
|
|
19
|
+
expect(config.meshUrl).toBe('http://localhost:7433');
|
|
20
|
+
expect(config.captureToolCalls).toBe(true); // default kept
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('DEFAULT_CONFIG is frozen shape', () => {
|
|
24
|
+
expect(DEFAULT_CONFIG.enabled).toBe(false);
|
|
25
|
+
expect(DEFAULT_CONFIG.dbPath).toContain('atoms.db');
|
|
26
|
+
expect(DEFAULT_CONFIG.meshUrl).toBe('https://mesh.osmosis.dev');
|
|
27
|
+
});
|
|
28
|
+
});
|