@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.
@@ -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"}
@@ -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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"}
@@ -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
@@ -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"}
@@ -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 {};
@@ -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
+ });