@oobe-protocol-labs/sap-mcp-server 0.9.22 → 0.9.35
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/.env.example +10 -0
- package/CHANGELOG.md +100 -6
- package/README.md +3 -3
- package/USER_DOCS/00_START_HERE.md +9 -1
- package/USER_DOCS/01_HOSTED_REMOTE_MCP.md +1 -1
- package/USER_DOCS/04_CLIENT_CONFIGS.md +3 -3
- package/USER_DOCS/06_DESKTOP_GUI_WIZARD.md +5 -5
- package/dist/config/env.d.ts +18 -0
- package/dist/config/env.d.ts.map +1 -1
- package/dist/config/env.js +14 -2
- package/dist/config/env.js.map +1 -1
- package/dist/config/mcp-client-injection.js +3 -3
- package/dist/config/paths.d.ts +9 -1
- package/dist/config/paths.d.ts.map +1 -1
- package/dist/config/paths.js +15 -1
- package/dist/config/paths.js.map +1 -1
- package/dist/config/wizard.d.ts.map +1 -1
- package/dist/config/wizard.js +3 -1
- package/dist/config/wizard.js.map +1 -1
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/constants.js +1 -1
- package/dist/core/logger.js +1 -1
- package/dist/memory/async-processor.d.ts +107 -0
- package/dist/memory/async-processor.d.ts.map +1 -0
- package/dist/memory/async-processor.js +204 -0
- package/dist/memory/async-processor.js.map +1 -0
- package/dist/memory/auto-record.d.ts +47 -0
- package/dist/memory/auto-record.d.ts.map +1 -0
- package/dist/memory/auto-record.js +133 -0
- package/dist/memory/auto-record.js.map +1 -0
- package/dist/memory/database.d.ts +103 -0
- package/dist/memory/database.d.ts.map +1 -0
- package/dist/memory/database.js +337 -0
- package/dist/memory/database.js.map +1 -0
- package/dist/memory/hermes-bridge.d.ts +70 -0
- package/dist/memory/hermes-bridge.d.ts.map +1 -0
- package/dist/memory/hermes-bridge.js +155 -0
- package/dist/memory/hermes-bridge.js.map +1 -0
- package/dist/memory/index.d.ts +23 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +22 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/memory-store.d.ts +76 -0
- package/dist/memory/memory-store.d.ts.map +1 -0
- package/dist/memory/memory-store.js +231 -0
- package/dist/memory/memory-store.js.map +1 -0
- package/dist/memory/stream-buffer-store.d.ts +69 -0
- package/dist/memory/stream-buffer-store.d.ts.map +1 -0
- package/dist/memory/stream-buffer-store.js +182 -0
- package/dist/memory/stream-buffer-store.js.map +1 -0
- package/dist/memory/tool-call-store.d.ts +68 -0
- package/dist/memory/tool-call-store.d.ts.map +1 -0
- package/dist/memory/tool-call-store.js +189 -0
- package/dist/memory/tool-call-store.js.map +1 -0
- package/dist/memory/types.d.ts +197 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +15 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/memory/utils.d.ts +30 -0
- package/dist/memory/utils.d.ts.map +1 -0
- package/dist/memory/utils.js +47 -0
- package/dist/memory/utils.js.map +1 -0
- package/dist/payments/hosted-tool-eligibility.d.ts.map +1 -1
- package/dist/payments/hosted-tool-eligibility.js +4 -6
- package/dist/payments/hosted-tool-eligibility.js.map +1 -1
- package/dist/payments/monetization-gate.d.ts +39 -0
- package/dist/payments/monetization-gate.d.ts.map +1 -1
- package/dist/payments/monetization-gate.js +165 -11
- package/dist/payments/monetization-gate.js.map +1 -1
- package/dist/payments/pricing.d.ts.map +1 -1
- package/dist/payments/pricing.js +69 -0
- package/dist/payments/pricing.js.map +1 -1
- package/dist/payments/x402-paid-call.js +1 -1
- package/dist/premium/builtin-plugins.d.ts +42 -0
- package/dist/premium/builtin-plugins.d.ts.map +1 -1
- package/dist/premium/builtin-plugins.js +91 -10
- package/dist/premium/builtin-plugins.js.map +1 -1
- package/dist/premium/manifest-builder.d.ts.map +1 -1
- package/dist/premium/manifest-builder.js +43 -7
- package/dist/premium/manifest-builder.js.map +1 -1
- package/dist/premium/meme-radar-capabilities.js +9 -9
- package/dist/premium/meme-radar-capabilities.js.map +1 -1
- package/dist/premium/plugin-validator.d.ts.map +1 -1
- package/dist/premium/plugin-validator.js +62 -1
- package/dist/premium/plugin-validator.js.map +1 -1
- package/dist/premium/provider-bridge.d.ts +25 -1
- package/dist/premium/provider-bridge.d.ts.map +1 -1
- package/dist/premium/provider-bridge.js +130 -20
- package/dist/premium/provider-bridge.js.map +1 -1
- package/dist/premium/session-manager.d.ts.map +1 -1
- package/dist/premium/session-manager.js +12 -3
- package/dist/premium/session-manager.js.map +1 -1
- package/dist/premium/tech-fundamentals-capabilities.js +5 -5
- package/dist/premium/tech-fundamentals-capabilities.js.map +1 -1
- package/dist/premium/trading-capabilities.js +5 -5
- package/dist/premium/trading-capabilities.js.map +1 -1
- package/dist/premium/webhook-engine.d.ts.map +1 -1
- package/dist/premium/webhook-engine.js +7 -1
- package/dist/premium/webhook-engine.js.map +1 -1
- package/dist/prompts/context/sap-agent-context.prompt.js +1 -1
- package/dist/prompts/payments/explain-x402-settlement.prompt.js +1 -1
- package/dist/remote/premium-routes.d.ts.map +1 -1
- package/dist/remote/premium-routes.js +3 -2
- package/dist/remote/premium-routes.js.map +1 -1
- package/dist/remote/public-home/sections.d.ts.map +1 -1
- package/dist/remote/public-home/sections.js +2 -3
- package/dist/remote/public-home/sections.js.map +1 -1
- package/dist/remote/public-home/styles.d.ts +1 -1
- package/dist/remote/public-home/styles.js +2 -2
- package/dist/remote/server.d.ts.map +1 -1
- package/dist/remote/server.js +19 -4
- package/dist/remote/server.js.map +1 -1
- package/dist/server/server-metadata.js +1 -1
- package/dist/strategies/strategy-store.d.ts +82 -0
- package/dist/strategies/strategy-store.d.ts.map +1 -0
- package/dist/strategies/strategy-store.js +165 -0
- package/dist/strategies/strategy-store.js.map +1 -0
- package/dist/tools/agent-start-tool.d.ts.map +1 -1
- package/dist/tools/agent-start-tool.js +97 -10
- package/dist/tools/agent-start-tool.js.map +1 -1
- package/dist/tools/client-sdk-tools.d.ts.map +1 -1
- package/dist/tools/client-sdk-tools.js +152 -9
- package/dist/tools/client-sdk-tools.js.map +1 -1
- package/dist/tools/estimate-tool-cost.js +1 -1
- package/dist/tools/estimate-tool-cost.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/magicblock-tools.js +5 -5
- package/dist/tools/magicblock-tools.js.map +1 -1
- package/dist/tools/memory-tools.d.ts +26 -0
- package/dist/tools/memory-tools.d.ts.map +1 -0
- package/dist/tools/memory-tools.js +533 -0
- package/dist/tools/memory-tools.js.map +1 -0
- package/dist/tools/perp-tools.d.ts +7 -9
- package/dist/tools/perp-tools.d.ts.map +1 -1
- package/dist/tools/perp-tools.js +386 -367
- package/dist/tools/perp-tools.js.map +1 -1
- package/dist/tools/premium-tools.d.ts.map +1 -1
- package/dist/tools/premium-tools.js +8 -2
- package/dist/tools/premium-tools.js.map +1 -1
- package/dist/tools/profile-tools.d.ts.map +1 -1
- package/dist/tools/profile-tools.js +18 -1
- package/dist/tools/profile-tools.js.map +1 -1
- package/dist/tools/quick-context-tool.d.ts.map +1 -1
- package/dist/tools/quick-context-tool.js +100 -9
- package/dist/tools/quick-context-tool.js.map +1 -1
- package/dist/tools/register-tools.d.ts.map +1 -1
- package/dist/tools/register-tools.js +3 -0
- package/dist/tools/register-tools.js.map +1 -1
- package/dist/tools/sap-sdk-tools.js +3 -3
- package/dist/tools/sap-sdk-tools.js.map +1 -1
- package/dist/tools/session-context-packet.d.ts +19 -0
- package/dist/tools/session-context-packet.d.ts.map +1 -0
- package/dist/tools/session-context-packet.js +256 -0
- package/dist/tools/session-context-packet.js.map +1 -0
- package/dist/tools/skills-tools.d.ts +10 -0
- package/dist/tools/skills-tools.d.ts.map +1 -1
- package/dist/tools/skills-tools.js +19 -0
- package/dist/tools/skills-tools.js.map +1 -1
- package/dist/tools/transaction-tools.d.ts +1 -0
- package/dist/tools/transaction-tools.d.ts.map +1 -1
- package/dist/tools/transaction-tools.js +248 -9
- package/dist/tools/transaction-tools.js.map +1 -1
- package/dist/tools/x402-paid-call-tool.d.ts.map +1 -1
- package/dist/tools/x402-paid-call-tool.js +55 -21
- package/dist/tools/x402-paid-call-tool.js.map +1 -1
- package/dist/wizard-core/desktop-flow.d.ts.map +1 -1
- package/dist/wizard-core/desktop-flow.js +4 -2
- package/dist/wizard-core/desktop-flow.js.map +1 -1
- package/docs/06_PAYMENTS_X402_AND_PAYSH.md +14 -5
- package/docs/07_ENDPOINTS_AND_CLIENTS.md +1 -1
- package/docs/09_TOOLS_SKILLS_AND_AGENT_GUIDE.md +13 -5
- package/docs/13_BOUNTY_PROGRAM_PROPOSAL.md +1 -1
- package/docs/14_DESKTOP_WIZARD_RELEASE.md +3 -3
- package/docs/15_DASHBOARD_SCREENSHARE_SCRIPT.md +1 -1
- package/docs/BRANCH_AND_CI.md +2 -2
- package/docs/README.md +1 -1
- package/package.json +3 -1
- package/server.json +3 -3
- package/skills/README.md +1 -1
- package/skills/sap-agentkit/SKILL.md +1 -2
- package/skills/sap-defi/SKILL.md +31 -2
- package/skills/sap-mcp/SKILL.md +24 -13
- package/skills/sap-mcp/TOOL_REFERENCE.md +2 -4
- package/skills/sap-payments-x402/SKILL.md +15 -3
- package/assets/logos/drift.svg +0 -12
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module memory/auto-record
|
|
3
|
+
* @description Automatic tool call recording hook.
|
|
4
|
+
*
|
|
5
|
+
* Wraps the MCP tool handler to automatically record every tool call in the
|
|
6
|
+
* local memory database — no agent action required. The hook measures
|
|
7
|
+
* latency, captures the outcome, and stores truncated input/output.
|
|
8
|
+
*
|
|
9
|
+
* The hook is designed to be non-blocking: if the memory DB is degraded or
|
|
10
|
+
* the recording fails, the tool call proceeds normally. Recording errors
|
|
11
|
+
* are logged but never propagated to the caller.
|
|
12
|
+
*/
|
|
13
|
+
import { toolCallStore } from './tool-call-store.js';
|
|
14
|
+
import { memoryDatabase } from './database.js';
|
|
15
|
+
import { logger } from '../core/logger.js';
|
|
16
|
+
/**
|
|
17
|
+
* @name recordToolCall
|
|
18
|
+
* @description Records a tool call execution in the memory database.
|
|
19
|
+
*
|
|
20
|
+
* This function is called after every tool handler executes. It captures:
|
|
21
|
+
* - toolName: the MCP tool name
|
|
22
|
+
* - input: JSON-serialized input (truncated to 8KB)
|
|
23
|
+
* - output: JSON-serialized output (truncated to 8KB)
|
|
24
|
+
* - outcome: 'success' | 'error' | 'partial'
|
|
25
|
+
* - latencyMs: wall-clock time from start to finish
|
|
26
|
+
* - costUsd: estimated from the pricing tier (if available)
|
|
27
|
+
* - txSignature: extracted from the output if present
|
|
28
|
+
*
|
|
29
|
+
* The function never throws — recording failures are logged and swallowed.
|
|
30
|
+
*
|
|
31
|
+
* @param toolName - The MCP tool name.
|
|
32
|
+
* @param input - The raw input object passed to the tool.
|
|
33
|
+
* @param output - The tool result text.
|
|
34
|
+
* @param isError - Whether the tool returned an error.
|
|
35
|
+
* @param latencyMs - Execution latency in milliseconds.
|
|
36
|
+
* @param sessionId - Optional session identifier.
|
|
37
|
+
* @param callerProfile - Optional signer profile.
|
|
38
|
+
*/
|
|
39
|
+
export function recordToolCall(toolName, input, output, isError, latencyMs, sessionId, callerProfile) {
|
|
40
|
+
// Skip recording for memory tools themselves — avoid infinite recursion.
|
|
41
|
+
if (toolName.startsWith('sap_memory_') || toolName.startsWith('sap_audit_') || toolName.startsWith('sap_strategy_') || toolName.startsWith('sap_stream_')) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Skip if the memory database is degraded.
|
|
45
|
+
if (memoryDatabase.isDegraded())
|
|
46
|
+
return;
|
|
47
|
+
try {
|
|
48
|
+
// Serialize input/output with truncation.
|
|
49
|
+
const inputJson = safeStringify(input, 8192);
|
|
50
|
+
const outputJson = safeStringify(output, 8192);
|
|
51
|
+
// Determine outcome.
|
|
52
|
+
const outcome = isError ? 'error' : determineOutcome(output);
|
|
53
|
+
// Extract transaction signature from output if present.
|
|
54
|
+
const txSignature = extractTxSignature(output);
|
|
55
|
+
// Record in the database.
|
|
56
|
+
toolCallStore.record({
|
|
57
|
+
toolName,
|
|
58
|
+
sessionId: sessionId ?? null,
|
|
59
|
+
callerProfile: callerProfile ?? null,
|
|
60
|
+
input: inputJson,
|
|
61
|
+
output: outputJson,
|
|
62
|
+
outcome,
|
|
63
|
+
costUsd: null, // Cost is tracked by the x402 gate, not here.
|
|
64
|
+
txSignature,
|
|
65
|
+
latencyMs,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
// Never propagate recording errors — the tool call already succeeded.
|
|
70
|
+
logger.debug('Auto-record failed', {
|
|
71
|
+
toolName,
|
|
72
|
+
error: error instanceof Error ? error.message : String(error),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* @name determineOutcome
|
|
78
|
+
* @description Classifies the tool call outcome from the output text.
|
|
79
|
+
* Checks for partial success indicators (e.g. "partial", "some failed", "retry").
|
|
80
|
+
* @internal
|
|
81
|
+
*/
|
|
82
|
+
function determineOutcome(output) {
|
|
83
|
+
const lower = output.toLowerCase();
|
|
84
|
+
if (lower.includes('"partial"') || lower.includes('"some failed"') || lower.includes('"retry-safe": false')) {
|
|
85
|
+
return 'partial';
|
|
86
|
+
}
|
|
87
|
+
return 'success';
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* @name extractTxSignature
|
|
91
|
+
* @description Extracts a Solana transaction signature from the tool output.
|
|
92
|
+
* Looks for common patterns like "signature": "..." or "txSignature": "...".
|
|
93
|
+
* @internal
|
|
94
|
+
*/
|
|
95
|
+
function extractTxSignature(output) {
|
|
96
|
+
// Try JSON parsing first.
|
|
97
|
+
try {
|
|
98
|
+
const parsed = JSON.parse(output);
|
|
99
|
+
if (typeof parsed['signature'] === 'string')
|
|
100
|
+
return parsed['signature'];
|
|
101
|
+
if (typeof parsed['txSignature'] === 'string')
|
|
102
|
+
return parsed['txSignature'];
|
|
103
|
+
if (typeof parsed['transactionSignature'] === 'string')
|
|
104
|
+
return parsed['transactionSignature'];
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// Not JSON — try regex.
|
|
108
|
+
}
|
|
109
|
+
// Regex fallback for base58 signatures (64-88 chars).
|
|
110
|
+
const match = output.match(/\b[1-9A-HJ-NP-Za-km-z]{64,88}\b/);
|
|
111
|
+
return match ? match[0] : null;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* @name safeStringify
|
|
115
|
+
* @description Safely serializes a value to JSON with truncation.
|
|
116
|
+
* Handles circular references and large objects.
|
|
117
|
+
* @internal
|
|
118
|
+
*/
|
|
119
|
+
function safeStringify(value, maxBytes) {
|
|
120
|
+
try {
|
|
121
|
+
const json = JSON.stringify(value);
|
|
122
|
+
if (!json)
|
|
123
|
+
return '';
|
|
124
|
+
const buf = Buffer.from(json, 'utf-8');
|
|
125
|
+
if (buf.length <= maxBytes)
|
|
126
|
+
return json;
|
|
127
|
+
return buf.subarray(0, maxBytes).toString('utf-8') + '...[truncated]';
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return String(value).slice(0, maxBytes);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=auto-record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-record.js","sourceRoot":"","sources":["../../src/memory/auto-record.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module memory/database
|
|
3
|
+
* @description Thread-safe SQLite database manager for the SAP MCP agent memory subsystem.
|
|
4
|
+
*
|
|
5
|
+
* Uses better-sqlite3 (synchronous, native, FTS5-enabled) with WAL mode for
|
|
6
|
+
* concurrent read access. The database is a singleton — one connection per process.
|
|
7
|
+
*
|
|
8
|
+
* Design principles:
|
|
9
|
+
* - Synchronous I/O: better-sqlite3 is synchronous, no async overhead, no callback hell.
|
|
10
|
+
* - WAL mode: readers don't block writers, crash-safe via WAL journal.
|
|
11
|
+
* - Prepared statements: cached for performance, reused across calls.
|
|
12
|
+
* - FTS5: inverted full-text index for relevance-ranked retrieval.
|
|
13
|
+
* - Schema versioning: idempotent migrations on init.
|
|
14
|
+
* - Graceful degradation: if the DB can't be opened, tools return empty results.
|
|
15
|
+
*/
|
|
16
|
+
import type { Database as DatabaseType, Statement } from 'better-sqlite3';
|
|
17
|
+
import type { MemoryConfig } from './types.js';
|
|
18
|
+
/**
|
|
19
|
+
* @name DEFAULT_CONFIG
|
|
20
|
+
* @description Default memory configuration. The DB is stored in
|
|
21
|
+
* ~/.config/mcp-sap/memory/ which is the standard SAP MCP config directory.
|
|
22
|
+
*/
|
|
23
|
+
export declare const DEFAULT_CONFIG: MemoryConfig;
|
|
24
|
+
/**
|
|
25
|
+
* @name MemoryDatabase
|
|
26
|
+
* @description Thread-safe singleton managing the SQLite connection for agent memory.
|
|
27
|
+
*
|
|
28
|
+
* The class lazily initializes the database on first access and caches prepared
|
|
29
|
+
* statements for performance. All methods are synchronous (better-sqlite3 is
|
|
30
|
+
* synchronous by design). The database runs in WAL mode for concurrent reads.
|
|
31
|
+
*
|
|
32
|
+
* If the database cannot be opened (e.g. disk full, permissions), the instance
|
|
33
|
+
* enters a degraded mode where all queries return empty results instead of
|
|
34
|
+
* crashing the MCP server.
|
|
35
|
+
*/
|
|
36
|
+
export declare class MemoryDatabase {
|
|
37
|
+
private db;
|
|
38
|
+
private readonly config;
|
|
39
|
+
private degraded;
|
|
40
|
+
private initialized;
|
|
41
|
+
private statements;
|
|
42
|
+
constructor(config?: Partial<MemoryConfig>);
|
|
43
|
+
/**
|
|
44
|
+
* @name init
|
|
45
|
+
* @description Initializes the database connection, creates the schema, and
|
|
46
|
+
* enables WAL mode. Called lazily on first query.
|
|
47
|
+
*/
|
|
48
|
+
init(): void;
|
|
49
|
+
/**
|
|
50
|
+
* @name getDb
|
|
51
|
+
* @description Returns the underlying Database instance. Throws if degraded.
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
getDb(): DatabaseType;
|
|
55
|
+
/**
|
|
56
|
+
* @name isDegraded
|
|
57
|
+
* @description Returns true if the database is in degraded mode (init failed).
|
|
58
|
+
*/
|
|
59
|
+
isDegraded(): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* @name getStatement
|
|
62
|
+
* @description Returns a cached prepared statement by key.
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
getStatement(key: string): Statement | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* @name close
|
|
68
|
+
* @description Closes the database connection gracefully.
|
|
69
|
+
*/
|
|
70
|
+
close(): void;
|
|
71
|
+
/**
|
|
72
|
+
* @name getDbSize
|
|
73
|
+
* @description Returns the database file size in bytes.
|
|
74
|
+
*/
|
|
75
|
+
getDbSize(): number;
|
|
76
|
+
/**
|
|
77
|
+
* @name runMigrations
|
|
78
|
+
* @description Runs idempotent schema migrations. Each migration checks if
|
|
79
|
+
* the current schema version is below the target and applies changes.
|
|
80
|
+
* @internal
|
|
81
|
+
*/
|
|
82
|
+
private runMigrations;
|
|
83
|
+
/**
|
|
84
|
+
* @name migrateToV1
|
|
85
|
+
* @description Creates the initial schema: tool_calls, agent_memory,
|
|
86
|
+
* stream_buffers, and their FTS5 indexes.
|
|
87
|
+
* @internal
|
|
88
|
+
*/
|
|
89
|
+
private migrateToV1;
|
|
90
|
+
/**
|
|
91
|
+
* @name prepareStatements
|
|
92
|
+
* @description Pre-compiles and caches frequently used SQL statements.
|
|
93
|
+
* @internal
|
|
94
|
+
*/
|
|
95
|
+
private prepareStatements;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* @name memoryDatabase
|
|
99
|
+
* @description Singleton instance of the memory database.
|
|
100
|
+
* Lazily initialized on first access. Shared across all memory tools.
|
|
101
|
+
*/
|
|
102
|
+
export declare const memoryDatabase: MemoryDatabase;
|
|
103
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/memory/database.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,QAAQ,IAAI,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAK1E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAS/C;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,YAQ5B,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,EAAE,CAA6B;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAqC;gBAE3C,MAAM,GAAE,OAAO,CAAC,YAAY,CAAM;IAI9C;;;;OAIG;IACI,IAAI,IAAI,IAAI;IA0CnB;;;;OAIG;IACI,KAAK,IAAI,YAAY;IAO5B;;;OAGG;IACI,UAAU,IAAI,OAAO;IAI5B;;;;OAIG;IACI,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAIvD;;;OAGG;IACI,KAAK,IAAI,IAAI;IAapB;;;OAGG;IACI,SAAS,IAAI,MAAM;IAU1B;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IA4BrB;;;;;OAKG;IACH,OAAO,CAAC,WAAW;IAkHnB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;CAyD1B;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,gBAAuB,CAAC"}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module memory/database
|
|
3
|
+
* @description Thread-safe SQLite database manager for the SAP MCP agent memory subsystem.
|
|
4
|
+
*
|
|
5
|
+
* Uses better-sqlite3 (synchronous, native, FTS5-enabled) with WAL mode for
|
|
6
|
+
* concurrent read access. The database is a singleton — one connection per process.
|
|
7
|
+
*
|
|
8
|
+
* Design principles:
|
|
9
|
+
* - Synchronous I/O: better-sqlite3 is synchronous, no async overhead, no callback hell.
|
|
10
|
+
* - WAL mode: readers don't block writers, crash-safe via WAL journal.
|
|
11
|
+
* - Prepared statements: cached for performance, reused across calls.
|
|
12
|
+
* - FTS5: inverted full-text index for relevance-ranked retrieval.
|
|
13
|
+
* - Schema versioning: idempotent migrations on init.
|
|
14
|
+
* - Graceful degradation: if the DB can't be opened, tools return empty results.
|
|
15
|
+
*/
|
|
16
|
+
import Database from 'better-sqlite3';
|
|
17
|
+
import { mkdirSync, statSync } from 'fs';
|
|
18
|
+
import { dirname, join } from 'path';
|
|
19
|
+
import { logger } from '../core/logger.js';
|
|
20
|
+
import { getMemoryDir } from '../config/paths.js';
|
|
21
|
+
/**
|
|
22
|
+
* @name SCHEMA_VERSION
|
|
23
|
+
* @description Current schema version. Increment when the schema changes.
|
|
24
|
+
* The database tracks this in the `_meta` table and runs migrations as needed.
|
|
25
|
+
*/
|
|
26
|
+
const SCHEMA_VERSION = 1;
|
|
27
|
+
/**
|
|
28
|
+
* @name DEFAULT_CONFIG
|
|
29
|
+
* @description Default memory configuration. The DB is stored in
|
|
30
|
+
* ~/.config/mcp-sap/memory/ which is the standard SAP MCP config directory.
|
|
31
|
+
*/
|
|
32
|
+
export const DEFAULT_CONFIG = {
|
|
33
|
+
dbPath: join(getMemoryDir(), 'agent-memory.db'),
|
|
34
|
+
enableWal: true,
|
|
35
|
+
maxPayloadBytes: 8192,
|
|
36
|
+
relevanceDecayPerDay: 0.01,
|
|
37
|
+
minRelevanceThreshold: 0.05,
|
|
38
|
+
toolCallRetentionDays: 90,
|
|
39
|
+
maxStreamBufferSize: 10_000,
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* @name MemoryDatabase
|
|
43
|
+
* @description Thread-safe singleton managing the SQLite connection for agent memory.
|
|
44
|
+
*
|
|
45
|
+
* The class lazily initializes the database on first access and caches prepared
|
|
46
|
+
* statements for performance. All methods are synchronous (better-sqlite3 is
|
|
47
|
+
* synchronous by design). The database runs in WAL mode for concurrent reads.
|
|
48
|
+
*
|
|
49
|
+
* If the database cannot be opened (e.g. disk full, permissions), the instance
|
|
50
|
+
* enters a degraded mode where all queries return empty results instead of
|
|
51
|
+
* crashing the MCP server.
|
|
52
|
+
*/
|
|
53
|
+
export class MemoryDatabase {
|
|
54
|
+
db = null;
|
|
55
|
+
config;
|
|
56
|
+
degraded = false;
|
|
57
|
+
initialized = false;
|
|
58
|
+
statements = new Map();
|
|
59
|
+
constructor(config = {}) {
|
|
60
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* @name init
|
|
64
|
+
* @description Initializes the database connection, creates the schema, and
|
|
65
|
+
* enables WAL mode. Called lazily on first query.
|
|
66
|
+
*/
|
|
67
|
+
init() {
|
|
68
|
+
if (this.initialized)
|
|
69
|
+
return;
|
|
70
|
+
this.initialized = true;
|
|
71
|
+
try {
|
|
72
|
+
// Ensure the directory exists.
|
|
73
|
+
const dir = dirname(this.config.dbPath);
|
|
74
|
+
mkdirSync(dir, { recursive: true });
|
|
75
|
+
// Open the database with better-sqlite3.
|
|
76
|
+
this.db = new Database(this.config.dbPath);
|
|
77
|
+
// Enable WAL mode for concurrent read access and crash safety.
|
|
78
|
+
if (this.config.enableWal) {
|
|
79
|
+
this.db.pragma('journal_mode = WAL');
|
|
80
|
+
this.db.pragma('synchronous = NORMAL');
|
|
81
|
+
this.db.pragma('wal_autocheckpoint = 1000');
|
|
82
|
+
}
|
|
83
|
+
// Set busy timeout to 5 seconds for concurrent access.
|
|
84
|
+
this.db.pragma('busy_timeout = 5000');
|
|
85
|
+
// Run schema migrations.
|
|
86
|
+
this.runMigrations();
|
|
87
|
+
// Prepare cached statements.
|
|
88
|
+
this.prepareStatements();
|
|
89
|
+
logger.info('Memory database initialized', {
|
|
90
|
+
path: this.config.dbPath,
|
|
91
|
+
wal: this.config.enableWal,
|
|
92
|
+
schemaVersion: SCHEMA_VERSION,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
logger.error('Memory database initialization failed — entering degraded mode', {
|
|
97
|
+
error: error instanceof Error ? error.message : String(error),
|
|
98
|
+
path: this.config.dbPath,
|
|
99
|
+
});
|
|
100
|
+
this.degraded = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* @name getDb
|
|
105
|
+
* @description Returns the underlying Database instance. Throws if degraded.
|
|
106
|
+
* @internal
|
|
107
|
+
*/
|
|
108
|
+
getDb() {
|
|
109
|
+
if (this.degraded || !this.db) {
|
|
110
|
+
throw new Error('Memory database is in degraded mode — queries return empty results.');
|
|
111
|
+
}
|
|
112
|
+
return this.db;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* @name isDegraded
|
|
116
|
+
* @description Returns true if the database is in degraded mode (init failed).
|
|
117
|
+
*/
|
|
118
|
+
isDegraded() {
|
|
119
|
+
return this.degraded;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* @name getStatement
|
|
123
|
+
* @description Returns a cached prepared statement by key.
|
|
124
|
+
* @internal
|
|
125
|
+
*/
|
|
126
|
+
getStatement(key) {
|
|
127
|
+
return this.statements.get(key);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* @name close
|
|
131
|
+
* @description Closes the database connection gracefully.
|
|
132
|
+
*/
|
|
133
|
+
close() {
|
|
134
|
+
if (this.db) {
|
|
135
|
+
try {
|
|
136
|
+
this.db.close();
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Best-effort close.
|
|
140
|
+
}
|
|
141
|
+
this.db = null;
|
|
142
|
+
}
|
|
143
|
+
this.statements.clear();
|
|
144
|
+
this.initialized = false;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* @name getDbSize
|
|
148
|
+
* @description Returns the database file size in bytes.
|
|
149
|
+
*/
|
|
150
|
+
getDbSize() {
|
|
151
|
+
try {
|
|
152
|
+
return statSync(this.config.dbPath).size;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// ── Schema Migrations ──────────────────────────────────────────────────────
|
|
159
|
+
/**
|
|
160
|
+
* @name runMigrations
|
|
161
|
+
* @description Runs idempotent schema migrations. Each migration checks if
|
|
162
|
+
* the current schema version is below the target and applies changes.
|
|
163
|
+
* @internal
|
|
164
|
+
*/
|
|
165
|
+
runMigrations() {
|
|
166
|
+
if (!this.db)
|
|
167
|
+
return;
|
|
168
|
+
// Create _meta table if it doesn't exist.
|
|
169
|
+
this.db.exec(`
|
|
170
|
+
CREATE TABLE IF NOT EXISTS _meta (
|
|
171
|
+
key TEXT PRIMARY KEY,
|
|
172
|
+
value TEXT NOT NULL
|
|
173
|
+
);
|
|
174
|
+
`);
|
|
175
|
+
// Get current schema version.
|
|
176
|
+
const versionRow = this.db.prepare('SELECT value FROM _meta WHERE key = ?').get('schema_version');
|
|
177
|
+
const currentVersion = versionRow ? parseInt(versionRow.value, 10) : 0;
|
|
178
|
+
if (currentVersion < 1) {
|
|
179
|
+
this.migrateToV1();
|
|
180
|
+
}
|
|
181
|
+
// Update schema version.
|
|
182
|
+
this.db.prepare('INSERT OR REPLACE INTO _meta (key, value) VALUES (?, ?)').run('schema_version', SCHEMA_VERSION.toString());
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* @name migrateToV1
|
|
186
|
+
* @description Creates the initial schema: tool_calls, agent_memory,
|
|
187
|
+
* stream_buffers, and their FTS5 indexes.
|
|
188
|
+
* @internal
|
|
189
|
+
*/
|
|
190
|
+
migrateToV1() {
|
|
191
|
+
if (!this.db)
|
|
192
|
+
return;
|
|
193
|
+
this.db.exec(`
|
|
194
|
+
-- Tool call history
|
|
195
|
+
CREATE TABLE IF NOT EXISTS tool_calls (
|
|
196
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
197
|
+
tool_name TEXT NOT NULL,
|
|
198
|
+
session_id TEXT,
|
|
199
|
+
caller_profile TEXT,
|
|
200
|
+
input TEXT,
|
|
201
|
+
output TEXT,
|
|
202
|
+
outcome TEXT NOT NULL DEFAULT 'success',
|
|
203
|
+
cost_usd REAL DEFAULT 0,
|
|
204
|
+
tx_signature TEXT,
|
|
205
|
+
latency_ms INTEGER,
|
|
206
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
207
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
CREATE INDEX IF NOT EXISTS idx_tool_calls_tool ON tool_calls(tool_name);
|
|
211
|
+
CREATE INDEX IF NOT EXISTS idx_tool_calls_session ON tool_calls(session_id);
|
|
212
|
+
CREATE INDEX IF NOT EXISTS idx_tool_calls_outcome ON tool_calls(outcome);
|
|
213
|
+
CREATE INDEX IF NOT EXISTS idx_tool_calls_created ON tool_calls(created_at);
|
|
214
|
+
|
|
215
|
+
-- FTS5 inverted index for tool call search
|
|
216
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS tool_calls_fts USING fts5(
|
|
217
|
+
tool_name,
|
|
218
|
+
input,
|
|
219
|
+
output,
|
|
220
|
+
content='tool_calls',
|
|
221
|
+
content_rowid='id'
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
-- Triggers to keep FTS index in sync with tool_calls
|
|
225
|
+
CREATE TRIGGER IF NOT EXISTS tool_calls_ai AFTER INSERT ON tool_calls BEGIN
|
|
226
|
+
INSERT INTO tool_calls_fts(rowid, tool_name, input, output)
|
|
227
|
+
VALUES (new.id, new.tool_name, new.input, new.output);
|
|
228
|
+
END;
|
|
229
|
+
|
|
230
|
+
CREATE TRIGGER IF NOT EXISTS tool_calls_ad AFTER DELETE ON tool_calls BEGIN
|
|
231
|
+
INSERT INTO tool_calls_fts(tool_calls_fts, rowid, tool_name, input, output)
|
|
232
|
+
VALUES ('delete', old.id, old.tool_name, old.input, old.output);
|
|
233
|
+
END;
|
|
234
|
+
|
|
235
|
+
CREATE TRIGGER IF NOT EXISTS tool_calls_au AFTER UPDATE ON tool_calls BEGIN
|
|
236
|
+
INSERT INTO tool_calls_fts(tool_calls_fts, rowid, tool_name, input, output)
|
|
237
|
+
VALUES ('delete', old.id, old.tool_name, old.input, old.output);
|
|
238
|
+
INSERT INTO tool_calls_fts(rowid, tool_name, input, output)
|
|
239
|
+
VALUES (new.id, new.tool_name, new.input, new.output);
|
|
240
|
+
END;
|
|
241
|
+
|
|
242
|
+
-- Agent memory — LLM-generated summaries
|
|
243
|
+
CREATE TABLE IF NOT EXISTS agent_memory (
|
|
244
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
245
|
+
memory_type TEXT NOT NULL,
|
|
246
|
+
category TEXT NOT NULL,
|
|
247
|
+
summary TEXT NOT NULL,
|
|
248
|
+
source_tool_calls TEXT,
|
|
249
|
+
relevance_score REAL DEFAULT 0.5,
|
|
250
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
251
|
+
expires_at TEXT,
|
|
252
|
+
last_accessed_at TEXT DEFAULT (datetime('now'))
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
CREATE INDEX IF NOT EXISTS idx_agent_memory_category ON agent_memory(category);
|
|
256
|
+
CREATE INDEX IF NOT EXISTS idx_agent_memory_relevance ON agent_memory(relevance_score);
|
|
257
|
+
CREATE INDEX IF NOT EXISTS idx_agent_memory_type ON agent_memory(memory_type);
|
|
258
|
+
|
|
259
|
+
-- FTS5 inverted index for agent memory search
|
|
260
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS agent_memory_fts USING fts5(
|
|
261
|
+
memory_type,
|
|
262
|
+
category,
|
|
263
|
+
summary,
|
|
264
|
+
content='agent_memory',
|
|
265
|
+
content_rowid='id'
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
-- Triggers to keep FTS index in sync with agent_memory
|
|
269
|
+
CREATE TRIGGER IF NOT EXISTS agent_memory_ai AFTER INSERT ON agent_memory BEGIN
|
|
270
|
+
INSERT INTO agent_memory_fts(rowid, memory_type, category, summary)
|
|
271
|
+
VALUES (new.id, new.memory_type, new.category, new.summary);
|
|
272
|
+
END;
|
|
273
|
+
|
|
274
|
+
CREATE TRIGGER IF NOT EXISTS agent_memory_ad AFTER DELETE ON agent_memory BEGIN
|
|
275
|
+
INSERT INTO agent_memory_fts(agent_memory_fts, rowid, memory_type, category, summary)
|
|
276
|
+
VALUES ('delete', old.id, old.memory_type, old.category, old.summary);
|
|
277
|
+
END;
|
|
278
|
+
|
|
279
|
+
CREATE TRIGGER IF NOT EXISTS agent_memory_au AFTER UPDATE ON agent_memory BEGIN
|
|
280
|
+
INSERT INTO agent_memory_fts(agent_memory_fts, rowid, memory_type, category, summary)
|
|
281
|
+
VALUES ('delete', old.id, old.memory_type, old.category, old.summary);
|
|
282
|
+
INSERT INTO agent_memory_fts(rowid, memory_type, category, summary)
|
|
283
|
+
VALUES (new.id, new.memory_type, new.category, new.summary);
|
|
284
|
+
END;
|
|
285
|
+
|
|
286
|
+
-- Stream buffer events
|
|
287
|
+
CREATE TABLE IF NOT EXISTS stream_buffers (
|
|
288
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
289
|
+
stream_type TEXT NOT NULL,
|
|
290
|
+
event_id TEXT NOT NULL,
|
|
291
|
+
event_type TEXT NOT NULL,
|
|
292
|
+
payload TEXT NOT NULL,
|
|
293
|
+
consumed INTEGER DEFAULT 0,
|
|
294
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
CREATE INDEX IF NOT EXISTS idx_stream_buffers_type ON stream_buffers(stream_type);
|
|
298
|
+
CREATE INDEX IF NOT EXISTS idx_stream_buffers_consumed ON stream_buffers(consumed);
|
|
299
|
+
CREATE INDEX IF NOT EXISTS idx_stream_buffers_created ON stream_buffers(created_at);
|
|
300
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_stream_buffers_event ON stream_buffers(stream_type, event_id);
|
|
301
|
+
`);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* @name prepareStatements
|
|
305
|
+
* @description Pre-compiles and caches frequently used SQL statements.
|
|
306
|
+
* @internal
|
|
307
|
+
*/
|
|
308
|
+
prepareStatements() {
|
|
309
|
+
if (!this.db)
|
|
310
|
+
return;
|
|
311
|
+
// Tool calls
|
|
312
|
+
this.statements.set('insert_tool_call', this.db.prepare(`INSERT INTO tool_calls (tool_name, session_id, caller_profile, input, output, outcome, cost_usd, tx_signature, latency_ms)
|
|
313
|
+
VALUES (@toolName, @sessionId, @callerProfile, @input, @output, @outcome, @costUsd, @txSignature, @latencyMs)`));
|
|
314
|
+
this.statements.set('count_tool_calls', this.db.prepare('SELECT COUNT(*) as count FROM tool_calls'));
|
|
315
|
+
this.statements.set('count_tool_calls_by_outcome', this.db.prepare('SELECT outcome, COUNT(*) as count FROM tool_calls GROUP BY outcome'));
|
|
316
|
+
this.statements.set('last_tool_call_at', this.db.prepare('SELECT created_at FROM tool_calls ORDER BY id DESC LIMIT 1'));
|
|
317
|
+
// Agent memory
|
|
318
|
+
this.statements.set('insert_memory', this.db.prepare(`INSERT INTO agent_memory (memory_type, category, summary, source_tool_calls, relevance_score, expires_at)
|
|
319
|
+
VALUES (@memoryType, @category, @summary, @sourceToolCalls, @relevanceScore, @expiresAt)`));
|
|
320
|
+
this.statements.set('count_memory', this.db.prepare('SELECT COUNT(*) as count FROM agent_memory'));
|
|
321
|
+
this.statements.set('count_memory_by_type', this.db.prepare('SELECT memory_type, COUNT(*) as count FROM agent_memory GROUP BY memory_type'));
|
|
322
|
+
this.statements.set('last_memory_at', this.db.prepare('SELECT created_at FROM agent_memory ORDER BY id DESC LIMIT 1'));
|
|
323
|
+
this.statements.set('update_memory_access', this.db.prepare('UPDATE agent_memory SET last_accessed_at = datetime(\'now\') WHERE id = ?'));
|
|
324
|
+
// Stream buffers
|
|
325
|
+
this.statements.set('count_pending_streams', this.db.prepare('SELECT COUNT(*) as count FROM stream_buffers WHERE consumed = 0'));
|
|
326
|
+
this.statements.set('insert_stream_event', this.db.prepare(`INSERT OR IGNORE INTO stream_buffers (stream_type, event_id, event_type, payload, consumed)
|
|
327
|
+
VALUES (@streamType, @eventId, @eventType, @payload, 0)`));
|
|
328
|
+
this.statements.set('consume_stream_events', this.db.prepare('UPDATE stream_buffers SET consumed = 1 WHERE stream_type = ? AND id <= ?'));
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* @name memoryDatabase
|
|
333
|
+
* @description Singleton instance of the memory database.
|
|
334
|
+
* Lazily initialized on first access. Shared across all memory tools.
|
|
335
|
+
*/
|
|
336
|
+
export const memoryDatabase = new MemoryDatabase();
|
|
337
|
+
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/memory/database.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module memory/hermes-bridge
|
|
3
|
+
* @description Cross-session integration with Hermes Agent session database.
|
|
4
|
+
*
|
|
5
|
+
* If the user has Hermes Agent installed (detected via ~/.hermes/ directory),
|
|
6
|
+
* the memory subsystem can query Hermes session history for cross-session
|
|
7
|
+
* context. This enables the agent to recall relevant conversations from
|
|
8
|
+
* previous Hermes sessions while keeping the data local.
|
|
9
|
+
*
|
|
10
|
+
* If Hermes is not installed, all bridge functions return empty results —
|
|
11
|
+
* the memory subsystem operates standalone.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* @name HermesBridge
|
|
15
|
+
* @description Bridges the SAP MCP memory subsystem with Hermes Agent session data.
|
|
16
|
+
*
|
|
17
|
+
* Detection: checks for ~/.hermes/ directory. If present, the bridge can query
|
|
18
|
+
* the Hermes SQLite session database for relevant past conversations.
|
|
19
|
+
*/
|
|
20
|
+
export declare class HermesBridge {
|
|
21
|
+
private readonly hermesDir;
|
|
22
|
+
private readonly hermesDbPath;
|
|
23
|
+
private available;
|
|
24
|
+
constructor();
|
|
25
|
+
/**
|
|
26
|
+
* @name isAvailable
|
|
27
|
+
* @description Checks whether Hermes Agent is installed on this machine.
|
|
28
|
+
* Caches the result to avoid repeated filesystem checks.
|
|
29
|
+
*/
|
|
30
|
+
isAvailable(): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* @name getHermesDir
|
|
33
|
+
* @description Returns the Hermes installation directory.
|
|
34
|
+
*/
|
|
35
|
+
getHermesDir(): string;
|
|
36
|
+
/**
|
|
37
|
+
* @name searchSessions
|
|
38
|
+
* @description Searches Hermes session history for relevant past conversations.
|
|
39
|
+
*
|
|
40
|
+
* This is a lightweight bridge — it reads the Hermes session database using
|
|
41
|
+
* the same better-sqlite3 driver (opening a read-only connection). The query
|
|
42
|
+
* uses FTS5 if the Hermes DB has it, otherwise falls back to LIKE search.
|
|
43
|
+
*
|
|
44
|
+
* @param query - Search query (natural language).
|
|
45
|
+
* @param limit - Max results. Default 5.
|
|
46
|
+
* @returns Array of session snippets with timestamps.
|
|
47
|
+
*/
|
|
48
|
+
searchSessions(query: string, limit?: number): Array<{
|
|
49
|
+
sessionId: string;
|
|
50
|
+
title: string;
|
|
51
|
+
snippet: string;
|
|
52
|
+
timestamp: string;
|
|
53
|
+
}>;
|
|
54
|
+
/**
|
|
55
|
+
* @name getRecentSessions
|
|
56
|
+
* @description Returns recent Hermes sessions for context injection.
|
|
57
|
+
* @param limit - Max sessions. Default 3.
|
|
58
|
+
*/
|
|
59
|
+
getRecentSessions(limit?: number): Array<{
|
|
60
|
+
sessionId: string;
|
|
61
|
+
title: string;
|
|
62
|
+
timestamp: string;
|
|
63
|
+
}>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* @name hermesBridge
|
|
67
|
+
* @description Singleton instance of the Hermes bridge.
|
|
68
|
+
*/
|
|
69
|
+
export declare const hermesBridge: HermesBridge;
|
|
70
|
+
//# sourceMappingURL=hermes-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hermes-bridge.d.ts","sourceRoot":"","sources":["../../src/memory/hermes-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH;;;;;;GAMG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,SAAS,CAAwB;;IAOzC;;;;OAIG;IACH,WAAW,IAAI,OAAO;IAWtB;;;OAGG;IACH,YAAY,IAAI,MAAM;IAItB;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAsDzH;;;;OAIG;IACH,iBAAiB,CAAC,KAAK,SAAI,GAAG,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CA0B7F;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,cAAqB,CAAC"}
|