@getplumb/openclaw-plugin 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Plumb Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # @getplumb/openclaw-plugin
2
+
3
+ > Plumb memory plugin for [OpenClaw](https://openclaw.ai)
4
+
5
+ Automatically ingests conversations and injects relevant memory context into every AI response — no "remember this" commands needed.
6
+
7
+ ## What it does
8
+
9
+ - **Auto-ingest:** every conversation turn is stored to your local Plumb DB after the response
10
+ - **Context injection:** relevant memory facts are injected into the system prompt before each response
11
+ - **Shadow mode:** try it without injection first — observe what would be injected
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install @getplumb/openclaw-plugin
17
+ ```
18
+
19
+ Then add to your `openclaw.json`:
20
+
21
+ ```json
22
+ {
23
+ "plugins": {
24
+ "plumb": {
25
+ "package": "@getplumb/openclaw-plugin",
26
+ "dbPath": "~/.plumb/memory.db",
27
+ "userId": "your-user-id",
28
+ "shadowMode": false
29
+ }
30
+ },
31
+ "slots": {
32
+ "memory": "plumb"
33
+ }
34
+ }
35
+ ```
36
+
37
+ ## Links
38
+
39
+ - [Docs](https://docs.getplumb.dev)
40
+ - [GitHub](https://github.com/getplumb/plumb)
41
+ - [getplumb.dev](https://getplumb.dev)
42
+
43
+ ## License
44
+
45
+ MIT
@@ -0,0 +1,14 @@
1
+ export interface PlumbPluginConfig {
2
+ /** Path to the @plumb/mcp-server binary (e.g. node_modules/.bin/plumb-mcp). */
3
+ mcpServerPath: string;
4
+ /** User identifier for multi-user setups. */
5
+ userId: string;
6
+ /** Whether the plugin is active. */
7
+ enabled: boolean;
8
+ /** Path to the Plumb database file. If not provided, defaults to ~/.plumb/memory.db */
9
+ dbPath?: string;
10
+ /** If true: retrieve but don't inject (for validation). Default: false. */
11
+ shadowMode: boolean;
12
+ }
13
+ export declare const DEFAULT_CONFIG: PlumbPluginConfig;
14
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,+EAA+E;IAC/E,aAAa,EAAE,MAAM,CAAC;IAEtB,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IAEf,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAC;IAEjB,uFAAuF;IACvF,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,2EAA2E;IAC3E,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,cAAc,EAAE,iBAK5B,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,7 @@
1
+ export const DEFAULT_CONFIG = {
2
+ mcpServerPath: 'plumb-mcp',
3
+ userId: 'default',
4
+ enabled: true,
5
+ shadowMode: false,
6
+ };
7
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAiBA,MAAM,CAAC,MAAM,cAAc,GAAsB;IAC/C,aAAa,EAAE,WAAW;IAC1B,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,KAAK;CAClB,CAAC"}
@@ -0,0 +1,17 @@
1
+ export interface ErrorLogEntry {
2
+ timestamp: string;
3
+ type: string;
4
+ message: string;
5
+ stack?: string;
6
+ context?: Record<string, unknown>;
7
+ }
8
+ /**
9
+ * Appends an error entry to ~/.plumb/errors.log (or PLUMB_DB_PATH parent if set).
10
+ * Format: JSONL (one JSON object per line).
11
+ *
12
+ * This function never throws — if the write fails, it silently logs to console.error only.
13
+ *
14
+ * @param entry - Error log entry with timestamp, type, message, and optional stack/context
15
+ */
16
+ export declare function appendError(entry: ErrorLogEntry): void;
17
+ //# sourceMappingURL=error-logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-logger.d.ts","sourceRoot":"","sources":["../src/error-logger.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAkBD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAetD"}
@@ -0,0 +1,40 @@
1
+ import { homedir } from 'node:os';
2
+ import { join, dirname } from 'node:path';
3
+ import { appendFileSync, mkdirSync } from 'node:fs';
4
+ /**
5
+ * Derives the error log path from the DB path (if set) or defaults to ~/.plumb/errors.log.
6
+ * If PLUMB_DB_PATH environment variable is set, we use its parent directory.
7
+ */
8
+ function getErrorLogPath() {
9
+ const dbPath = process.env.PLUMB_DB_PATH;
10
+ if (dbPath) {
11
+ // Use the parent directory of the DB path
12
+ return join(dirname(dbPath), 'errors.log');
13
+ }
14
+ // Default: ~/.plumb/errors.log
15
+ return join(homedir(), '.plumb', 'errors.log');
16
+ }
17
+ /**
18
+ * Appends an error entry to ~/.plumb/errors.log (or PLUMB_DB_PATH parent if set).
19
+ * Format: JSONL (one JSON object per line).
20
+ *
21
+ * This function never throws — if the write fails, it silently logs to console.error only.
22
+ *
23
+ * @param entry - Error log entry with timestamp, type, message, and optional stack/context
24
+ */
25
+ export function appendError(entry) {
26
+ try {
27
+ const logPath = getErrorLogPath();
28
+ const logDir = dirname(logPath);
29
+ // Ensure directory exists
30
+ mkdirSync(logDir, { recursive: true });
31
+ // Write JSONL: one JSON object per line
32
+ const line = JSON.stringify(entry) + '\n';
33
+ appendFileSync(logPath, line, 'utf-8');
34
+ }
35
+ catch (err) {
36
+ // Non-blocking: if the log write itself fails, just console.error
37
+ console.error('[plumb/error-logger] Failed to write error log:', err);
38
+ }
39
+ }
40
+ //# sourceMappingURL=error-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-logger.js","sourceRoot":"","sources":["../src/error-logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAUpD;;;GAGG;AACH,SAAS,eAAe;IACtB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IAEzC,IAAI,MAAM,EAAE,CAAC;QACX,0CAA0C;QAC1C,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC;IAC7C,CAAC;IAED,+BAA+B;IAC/B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,KAAoB;IAC9C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,eAAe,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAEhC,0BAA0B;QAC1B,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEvC,wCAAwC;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;QAC1C,cAAc,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,kEAAkE;QAClE,OAAO,CAAC,KAAK,CAAC,iDAAiD,EAAE,GAAG,CAAC,CAAC;IACxE,CAAC;AACH,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { LocalStore } from '@getplumb/core';
2
+ type PluginHookLlmOutputEvent = {
3
+ runId: string;
4
+ sessionId: string;
5
+ provider: string;
6
+ model: string;
7
+ assistantTexts: string[];
8
+ usage?: {
9
+ input?: number;
10
+ output?: number;
11
+ cacheRead?: number;
12
+ cacheWrite?: number;
13
+ total?: number;
14
+ };
15
+ };
16
+ type PluginHookAgentContext = {
17
+ agentId?: string;
18
+ sessionKey?: string;
19
+ sessionId?: string;
20
+ workspaceDir?: string;
21
+ messageProvider?: string;
22
+ };
23
+ /**
24
+ * Creates a hook handler that ingests every LLM exchange into the local store.
25
+ *
26
+ * Fire-and-forget pattern: returns void synchronously, ingests in background.
27
+ * Errors are caught and logged silently to avoid disrupting the agent flow.
28
+ */
29
+ export declare function createPostExchangeHook(store: LocalStore, userId: string): (event: PluginHookLlmOutputEvent, ctx: PluginHookAgentContext) => void;
30
+ export {};
31
+ //# sourceMappingURL=post-exchange.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-exchange.d.ts","sourceRoot":"","sources":["../../src/hooks/post-exchange.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAIjD,KAAK,wBAAwB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE;QACN,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,IAC9D,OAAO,wBAAwB,EAAE,KAAK,sBAAsB,KAAG,IAAI,CA4C5E"}
@@ -0,0 +1,52 @@
1
+ import { appendError } from '../error-logger.js';
2
+ /**
3
+ * Creates a hook handler that ingests every LLM exchange into the local store.
4
+ *
5
+ * Fire-and-forget pattern: returns void synchronously, ingests in background.
6
+ * Errors are caught and logged silently to avoid disrupting the agent flow.
7
+ */
8
+ export function createPostExchangeHook(store, userId) {
9
+ return (event, ctx) => {
10
+ const exchange = {
11
+ id: crypto.randomUUID(),
12
+ userId,
13
+ sessionId: ctx.sessionId ?? event.sessionId ?? crypto.randomUUID(),
14
+ sessionLabel: ctx.sessionKey,
15
+ userMessage: event.prompt ?? '',
16
+ agentResponse: event.assistantTexts.join('\n'),
17
+ timestamp: new Date().toISOString(),
18
+ source: 'openclaw',
19
+ };
20
+ // Fire-and-forget ingest — never blocks the hook
21
+ (async () => {
22
+ try {
23
+ await store.ingest({
24
+ userMessage: exchange.userMessage,
25
+ agentResponse: exchange.agentResponse,
26
+ timestamp: new Date(exchange.timestamp),
27
+ source: exchange.source,
28
+ sessionId: exchange.sessionId,
29
+ ...(exchange.sessionLabel && { sessionLabel: exchange.sessionLabel }),
30
+ });
31
+ }
32
+ catch (e) {
33
+ // Log error to ~/.plumb/errors.log for debugging
34
+ const errorEntry = {
35
+ timestamp: new Date().toISOString(),
36
+ type: 'ingest_error',
37
+ message: e instanceof Error ? e.message : String(e),
38
+ context: {
39
+ sessionId: exchange.sessionId,
40
+ userId,
41
+ source: exchange.source,
42
+ },
43
+ ...(e instanceof Error && e.stack ? { stack: e.stack } : {}),
44
+ };
45
+ appendError(errorEntry);
46
+ // Also log to console for immediate visibility during development
47
+ console.debug('[plumb] ingest error:', e);
48
+ }
49
+ })();
50
+ };
51
+ }
52
+ //# sourceMappingURL=post-exchange.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-exchange.js","sourceRoot":"","sources":["../../src/hooks/post-exchange.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AA0BjD;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAiB,EAAE,MAAc;IACtE,OAAO,CAAC,KAA+B,EAAE,GAA2B,EAAQ,EAAE;QAC5E,MAAM,QAAQ,GAAG;YACf,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;YACvB,MAAM;YACN,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,IAAI,MAAM,CAAC,UAAU,EAAE;YAClE,YAAY,EAAE,GAAG,CAAC,UAAU;YAC5B,WAAW,EAAG,KAAa,CAAC,MAAM,IAAI,EAAE;YACxC,aAAa,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;YAC9C,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,MAAM,EAAE,UAAmB;SAC5B,CAAC;QAEF,iDAAiD;QACjD,CAAC,KAAK,IAAI,EAAE;YACV,IAAI,CAAC;gBACH,MAAM,KAAK,CAAC,MAAM,CAAC;oBACjB,WAAW,EAAE,QAAQ,CAAC,WAAW;oBACjC,aAAa,EAAE,QAAQ,CAAC,aAAa;oBACrC,SAAS,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;oBACvC,MAAM,EAAE,QAAQ,CAAC,MAAM;oBACvB,SAAS,EAAE,QAAQ,CAAC,SAAS;oBAC7B,GAAG,CAAC,QAAQ,CAAC,YAAY,IAAI,EAAE,YAAY,EAAE,QAAQ,CAAC,YAAY,EAAE,CAAC;iBACtE,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,CAAU,EAAE,CAAC;gBACpB,iDAAiD;gBACjD,MAAM,UAAU,GAAG;oBACjB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACnC,IAAI,EAAE,cAAc;oBACpB,OAAO,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;oBACnD,OAAO,EAAE;wBACP,SAAS,EAAE,QAAQ,CAAC,SAAS;wBAC7B,MAAM;wBACN,MAAM,EAAE,QAAQ,CAAC,MAAM;qBACxB;oBACD,GAAG,CAAC,CAAC,YAAY,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC7D,CAAC;gBAEF,WAAW,CAAC,UAAU,CAAC,CAAC;gBAExB,kEAAkE;gBAClE,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,CAAC,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;IACP,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { LocalStore } from '@getplumb/core';
2
+ import type { NudgeManager } from '../nudge.js';
3
+ type PluginHookBeforePromptBuildEvent = {
4
+ prompt: string;
5
+ messages: unknown[];
6
+ };
7
+ type PluginHookBeforePromptBuildResult = {
8
+ systemPrompt?: string;
9
+ prependContext?: string;
10
+ };
11
+ type PluginHookAgentContext = {
12
+ agentId?: string;
13
+ sessionKey?: string;
14
+ sessionId?: string;
15
+ workspaceDir?: string;
16
+ messageProvider?: string;
17
+ };
18
+ /**
19
+ * Creates a hook handler that retrieves and injects memory context before each agent response.
20
+ *
21
+ * The hook queries the store with the incoming user message, formats the retrieved memories
22
+ * into a [PLUMB MEMORY] block, and prepends it to the system prompt.
23
+ *
24
+ * @param store LocalStore instance for memory retrieval
25
+ * @param nudgeManager NudgeManager instance for contextual upgrade nudges
26
+ * @param shadowMode If true, retrieves and logs what would be injected but doesn't actually inject
27
+ * @returns Hook handler for before_prompt_build event
28
+ */
29
+ export declare function createPreResponseHook(store: LocalStore | null, nudgeManager: NudgeManager | null, shadowMode?: boolean): (event: PluginHookBeforePromptBuildEvent, ctx: PluginHookAgentContext) => Promise<PluginHookBeforePromptBuildResult | void>;
30
+ export {};
31
+ //# sourceMappingURL=pre-response.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pre-response.d.ts","sourceRoot":"","sources":["../../src/hooks/pre-response.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,KAAK,gCAAgC,GAAG;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB,CAAC;AAEF,KAAK,iCAAiC,GAAG;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAIF;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,UAAU,GAAG,IAAI,EACxB,YAAY,EAAE,YAAY,GAAG,IAAI,EACjC,UAAU,UAAQ,IAGhB,OAAO,gCAAgC,EACvC,KAAK,sBAAsB,KAC1B,OAAO,CAAC,iCAAiC,GAAG,IAAI,CAAC,CAgFrD"}
@@ -0,0 +1,81 @@
1
+ import { buildMemoryContext, formatContextBlock } from '@getplumb/core';
2
+ const INJECTION_TIMEOUT_MS = 800;
3
+ /**
4
+ * Creates a hook handler that retrieves and injects memory context before each agent response.
5
+ *
6
+ * The hook queries the store with the incoming user message, formats the retrieved memories
7
+ * into a [PLUMB MEMORY] block, and prepends it to the system prompt.
8
+ *
9
+ * @param store LocalStore instance for memory retrieval
10
+ * @param nudgeManager NudgeManager instance for contextual upgrade nudges
11
+ * @param shadowMode If true, retrieves and logs what would be injected but doesn't actually inject
12
+ * @returns Hook handler for before_prompt_build event
13
+ */
14
+ export function createPreResponseHook(store, nudgeManager, shadowMode = false) {
15
+ return async (event, ctx) => {
16
+ if (!store) {
17
+ return;
18
+ }
19
+ let formattedContext = '';
20
+ let nudgeText = null;
21
+ try {
22
+ // Race between retrieval and timeout
23
+ const memoryContext = await Promise.race([
24
+ buildMemoryContext(event.prompt, store),
25
+ new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), INJECTION_TIMEOUT_MS)),
26
+ ]);
27
+ // Format the memory context into a prompt block
28
+ formattedContext = formatContextBlock(memoryContext);
29
+ }
30
+ catch (e) {
31
+ // Handle timeout silently — never slow down a response
32
+ if (e instanceof Error && e.message === 'timeout') {
33
+ console.warn('[plumb] memory retrieval timeout — skipping injection');
34
+ }
35
+ }
36
+ // Check for nudges
37
+ if (nudgeManager && ctx.sessionId) {
38
+ try {
39
+ // Check for 'second_integration' trigger
40
+ const secondIntegrationNudge = nudgeManager.checkSecondIntegration(store.db, store.userId, ctx.sessionId);
41
+ if (secondIntegrationNudge) {
42
+ nudgeText = secondIntegrationNudge;
43
+ // Record the nudge so it won't fire again
44
+ nudgeManager.recordNudge(store.db, 'second_integration');
45
+ }
46
+ }
47
+ catch (e) {
48
+ console.warn('[plumb] nudge check failed:', e);
49
+ }
50
+ }
51
+ // Build the final block
52
+ let block = '';
53
+ if (formattedContext && formattedContext.trim()) {
54
+ block = formattedContext;
55
+ }
56
+ if (nudgeText) {
57
+ if (block) {
58
+ // Append nudge after memory results
59
+ block = `${block}\n\n---\n${nudgeText}`;
60
+ }
61
+ else {
62
+ // Nudge is the sole content
63
+ block = nudgeText;
64
+ }
65
+ }
66
+ // Skip injection if no content
67
+ if (!block || !block.trim()) {
68
+ return;
69
+ }
70
+ // Wrap in [PLUMB MEMORY] delimiters
71
+ const finalBlock = `[PLUMB MEMORY]\n${block}\n[/PLUMB MEMORY]`;
72
+ // In shadow mode, log what would be injected but don't actually inject
73
+ if (shadowMode) {
74
+ console.debug('[plumb] shadow mode — would inject:', finalBlock.slice(0, 200));
75
+ return;
76
+ }
77
+ // Return the block to be prepended to the system prompt
78
+ return { prependContext: finalBlock };
79
+ };
80
+ }
81
+ //# sourceMappingURL=pre-response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pre-response.js","sourceRoot":"","sources":["../../src/hooks/pre-response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAuBxE,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAEjC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAwB,EACxB,YAAiC,EACjC,UAAU,GAAG,KAAK;IAElB,OAAO,KAAK,EACV,KAAuC,EACvC,GAA2B,EACwB,EAAE;QACrD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO;QACT,CAAC;QAED,IAAI,gBAAgB,GAAG,EAAE,CAAC;QAC1B,IAAI,SAAS,GAAkB,IAAI,CAAC;QAEpC,IAAI,CAAC;YACH,qCAAqC;YACrC,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;gBACvC,kBAAkB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC;gBACvC,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC/B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,oBAAoB,CAAC,CACrE;aACF,CAAC,CAAC;YAEH,gDAAgD;YAChD,gBAAgB,GAAG,kBAAkB,CAAC,aAAa,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,CAAU,EAAE,CAAC;YACpB,uDAAuD;YACvD,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;gBAClD,OAAO,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAC;YACxE,CAAC;QACH,CAAC;QAED,mBAAmB;QACnB,IAAI,YAAY,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YAClC,IAAI,CAAC;gBACH,yCAAyC;gBACzC,MAAM,sBAAsB,GAAG,YAAY,CAAC,sBAAsB,CAChE,KAAK,CAAC,EAAE,EACR,KAAK,CAAC,MAAM,EACZ,GAAG,CAAC,SAAS,CACd,CAAC;gBAEF,IAAI,sBAAsB,EAAE,CAAC;oBAC3B,SAAS,GAAG,sBAAsB,CAAC;oBACnC,0CAA0C;oBAC1C,YAAY,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,oBAAoB,CAAC,CAAC;gBAC3D,CAAC;YACH,CAAC;YAAC,OAAO,CAAU,EAAE,CAAC;gBACpB,OAAO,CAAC,IAAI,CAAC,6BAA6B,EAAE,CAAC,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;QAED,wBAAwB;QACxB,IAAI,KAAK,GAAG,EAAE,CAAC;QAEf,IAAI,gBAAgB,IAAI,gBAAgB,CAAC,IAAI,EAAE,EAAE,CAAC;YAChD,KAAK,GAAG,gBAAgB,CAAC;QAC3B,CAAC;QAED,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,KAAK,EAAE,CAAC;gBACV,oCAAoC;gBACpC,KAAK,GAAG,GAAG,KAAK,YAAY,SAAS,EAAE,CAAC;YAC1C,CAAC;iBAAM,CAAC;gBACN,4BAA4B;gBAC5B,KAAK,GAAG,SAAS,CAAC;YACpB,CAAC;QACH,CAAC;QAED,+BAA+B;QAC/B,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YAC5B,OAAO;QACT,CAAC;QAED,oCAAoC;QACpC,MAAM,UAAU,GAAG,mBAAmB,KAAK,mBAAmB,CAAC;QAE/D,uEAAuE;QACvE,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YAC/E,OAAO;QACT,CAAC;QAED,wDAAwD;QACxD,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,CAAC;IACxC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,26 @@
1
+ export { PlumbPluginConfig, DEFAULT_CONFIG } from './config.js';
2
+ export { PlumbMcpClient, MemorySearchResult, MemoryStoreResult, MemoryStatusResult, } from './mcp-client.js';
3
+ export { plugin } from './plugin-module.js';
4
+ export { createPostExchangeHook } from './hooks/post-exchange.js';
5
+ import type { PlumbPluginConfig } from './config.js';
6
+ import { PlumbMcpClient } from './mcp-client.js';
7
+ /**
8
+ * Main entry point for the Plumb OpenClaw plugin.
9
+ *
10
+ * Manages the lifecycle of the MCP client connection to the local
11
+ * plumb memory server. Pipeline hooks (ingest after exchange, inject
12
+ * before response) are implemented via plugin-module.ts (T-010) and
13
+ * will be enhanced in T-011.
14
+ */
15
+ export declare class PlumbPlugin {
16
+ readonly config: PlumbPluginConfig;
17
+ private mcpClient;
18
+ constructor(config?: Partial<PlumbPluginConfig>);
19
+ /** Connect to the MCP server. No-op if disabled. */
20
+ start(): Promise<void>;
21
+ /** Get the underlying MCP client (null if not started or disabled). */
22
+ get client(): PlumbMcpClient | null;
23
+ /** Disconnect and clean up. */
24
+ stop(): Promise<void>;
25
+ }
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;;;;GAOG;AACH,qBAAa,WAAW;IACtB,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,OAAO,CAAC,SAAS,CAA+B;gBAEpC,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC;IAI/C,oDAAoD;IAC9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,uEAAuE;IACvE,IAAI,MAAM,IAAI,cAAc,GAAG,IAAI,CAElC;IAED,+BAA+B;IACzB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAM5B"}
package/dist/index.js ADDED
@@ -0,0 +1,40 @@
1
+ export { DEFAULT_CONFIG } from './config.js';
2
+ export { PlumbMcpClient, } from './mcp-client.js';
3
+ export { plugin } from './plugin-module.js';
4
+ export { createPostExchangeHook } from './hooks/post-exchange.js';
5
+ import { DEFAULT_CONFIG } from './config.js';
6
+ import { PlumbMcpClient } from './mcp-client.js';
7
+ /**
8
+ * Main entry point for the Plumb OpenClaw plugin.
9
+ *
10
+ * Manages the lifecycle of the MCP client connection to the local
11
+ * plumb memory server. Pipeline hooks (ingest after exchange, inject
12
+ * before response) are implemented via plugin-module.ts (T-010) and
13
+ * will be enhanced in T-011.
14
+ */
15
+ export class PlumbPlugin {
16
+ config;
17
+ mcpClient = null;
18
+ constructor(config) {
19
+ this.config = { ...DEFAULT_CONFIG, ...config };
20
+ }
21
+ /** Connect to the MCP server. No-op if disabled. */
22
+ async start() {
23
+ if (!this.config.enabled)
24
+ return;
25
+ this.mcpClient = new PlumbMcpClient(this.config.mcpServerPath);
26
+ await this.mcpClient.connect();
27
+ }
28
+ /** Get the underlying MCP client (null if not started or disabled). */
29
+ get client() {
30
+ return this.mcpClient;
31
+ }
32
+ /** Disconnect and clean up. */
33
+ async stop() {
34
+ if (this.mcpClient) {
35
+ await this.mcpClient.close();
36
+ this.mcpClient = null;
37
+ }
38
+ }
39
+ }
40
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,cAAc,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EACL,cAAc,GAIf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,OAAO,WAAW;IACb,MAAM,CAAoB;IAC3B,SAAS,GAA0B,IAAI,CAAC;IAEhD,YAAY,MAAmC;QAC7C,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;IACjD,CAAC;IAED,oDAAoD;IACpD,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO;QACjC,IAAI,CAAC,SAAS,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QAC/D,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC;IAED,uEAAuE;IACvE,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,+BAA+B;IAC/B,KAAK,CAAC,IAAI;QACR,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,41 @@
1
+ /** A single search result returned by the MCP server's memory_search tool. */
2
+ export interface MemorySearchResult {
3
+ readonly fact: string;
4
+ readonly confidence: number;
5
+ readonly age_in_days: number;
6
+ readonly source_session_label?: string;
7
+ readonly layer: 'facts' | 'raw_log';
8
+ }
9
+ /** Result of a memory_store call. */
10
+ export interface MemoryStoreResult {
11
+ readonly fact_id: string;
12
+ }
13
+ /** Status shape returned by the MCP server (ISO string for dates). */
14
+ export interface MemoryStatusResult {
15
+ readonly factCount: number;
16
+ readonly rawLogCount: number;
17
+ readonly lastIngestion: string | null;
18
+ readonly storageBytes: number;
19
+ }
20
+ /**
21
+ * Thin wrapper around the MCP SDK Client that spawns the plumb MCP server
22
+ * as a child process via stdio and exposes typed helpers for the memory tools.
23
+ */
24
+ export declare class PlumbMcpClient {
25
+ private client;
26
+ private transport;
27
+ private _connected;
28
+ constructor(mcpServerPath: string);
29
+ /** Spawn the MCP server and complete the handshake. */
30
+ connect(): Promise<void>;
31
+ get connected(): boolean;
32
+ /** Search memory for relevant facts / raw log entries. */
33
+ search(query: string, limit?: number): Promise<MemorySearchResult[]>;
34
+ /** Store a new piece of content in the memory layer. */
35
+ store(content: string, source: string): Promise<MemoryStoreResult>;
36
+ /** Get memory store statistics. */
37
+ status(): Promise<MemoryStatusResult>;
38
+ /** Disconnect from the MCP server and kill the child process. */
39
+ close(): Promise<void>;
40
+ }
41
+ //# sourceMappingURL=mcp-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client.d.ts","sourceRoot":"","sources":["../src/mcp-client.ts"],"names":[],"mappings":"AAIA,8EAA8E;AAC9E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAAC;CACrC;AAED,qCAAqC;AACrC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,sEAAsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,UAAU,CAAS;gBAEf,aAAa,EAAE,MAAM;IAWjC,uDAAuD;IACjD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK9B,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,0DAA0D;IACpD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAS1E,wDAAwD;IAClD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IASxE,mCAAmC;IAC7B,MAAM,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAM3C,iEAAiE;IAC3D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B"}
@@ -0,0 +1,64 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
3
+ /**
4
+ * Thin wrapper around the MCP SDK Client that spawns the plumb MCP server
5
+ * as a child process via stdio and exposes typed helpers for the memory tools.
6
+ */
7
+ export class PlumbMcpClient {
8
+ client;
9
+ transport;
10
+ _connected = false;
11
+ constructor(mcpServerPath) {
12
+ this.client = new Client({ name: 'plumb-openclaw-plugin', version: '0.1.0' });
13
+ this.transport = new StdioClientTransport({
14
+ command: mcpServerPath,
15
+ stderr: 'pipe',
16
+ });
17
+ }
18
+ /** Spawn the MCP server and complete the handshake. */
19
+ async connect() {
20
+ await this.client.connect(this.transport);
21
+ this._connected = true;
22
+ }
23
+ get connected() {
24
+ return this._connected;
25
+ }
26
+ /** Search memory for relevant facts / raw log entries. */
27
+ async search(query, limit) {
28
+ const args = { query };
29
+ if (limit !== undefined)
30
+ args.limit = limit;
31
+ const result = await this.client.callTool({ name: 'memory_search', arguments: args });
32
+ const text = extractText(result);
33
+ return JSON.parse(text);
34
+ }
35
+ /** Store a new piece of content in the memory layer. */
36
+ async store(content, source) {
37
+ const result = await this.client.callTool({
38
+ name: 'memory_store',
39
+ arguments: { content, source },
40
+ });
41
+ const text = extractText(result);
42
+ return JSON.parse(text);
43
+ }
44
+ /** Get memory store statistics. */
45
+ async status() {
46
+ const result = await this.client.callTool({ name: 'memory_status', arguments: {} });
47
+ const text = extractText(result);
48
+ return JSON.parse(text);
49
+ }
50
+ /** Disconnect from the MCP server and kill the child process. */
51
+ async close() {
52
+ this._connected = false;
53
+ await this.transport.close();
54
+ }
55
+ }
56
+ /** Extract the first text content block from an MCP tool result. */
57
+ function extractText(result) {
58
+ const content = result.content;
59
+ const block = content.find((c) => c.type === 'text');
60
+ if (!block?.text)
61
+ throw new Error('MCP tool returned no text content');
62
+ return block.text;
63
+ }
64
+ //# sourceMappingURL=mcp-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client.js","sourceRoot":"","sources":["../src/mcp-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAyBjF;;;GAGG;AACH,MAAM,OAAO,cAAc;IACjB,MAAM,CAAS;IACf,SAAS,CAAuB;IAChC,UAAU,GAAG,KAAK,CAAC;IAE3B,YAAY,aAAqB;QAC/B,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CACtB,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,OAAO,EAAE,CACpD,CAAC;QAEF,IAAI,CAAC,SAAS,GAAG,IAAI,oBAAoB,CAAC;YACxC,OAAO,EAAE,aAAa;YACtB,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;IACL,CAAC;IAED,uDAAuD;IACvD,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC1C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,0DAA0D;IAC1D,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,KAAc;QACxC,MAAM,IAAI,GAA4B,EAAE,KAAK,EAAE,CAAC;QAChD,IAAI,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QAE5C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtF,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAyB,CAAC;IAClD,CAAC;IAED,wDAAwD;IACxD,KAAK,CAAC,KAAK,CAAC,OAAe,EAAE,MAAc;QACzC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;YACxC,IAAI,EAAE,cAAc;YACpB,SAAS,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;SAC/B,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAsB,CAAC;IAC/C,CAAC;IAED,mCAAmC;IACnC,KAAK,CAAC,MAAM;QACV,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;QACpF,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAuB,CAAC;IAChD,CAAC;IAED,iEAAiE;IACjE,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IAC/B,CAAC;CACF;AAED,oEAAoE;AACpE,SAAS,WAAW,CAAC,MAA+C;IAClE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAiD,CAAC;IACzE,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IACrD,IAAI,CAAC,KAAK,EAAE,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvE,OAAO,KAAK,CAAC,IAAI,CAAC;AACpB,CAAC"}
@@ -0,0 +1,49 @@
1
+ export type NudgeTriggerType = 'second_integration' | 'mcp_downtime';
2
+ type Database = any;
3
+ /**
4
+ * Manages one-time contextual upgrade nudges to the user.
5
+ * Each trigger type fires exactly once per install, tracked in the nudge_log table.
6
+ */
7
+ export declare class NudgeManager {
8
+ /** Track which sessions have been seen this runtime to avoid redundant checks */
9
+ private seenSessions;
10
+ /** Track MCP downtime start timestamp (in-memory, not persisted) */
11
+ private mcpDowntimeStart;
12
+ /** Threshold in milliseconds for MCP downtime trigger (5 minutes) */
13
+ private readonly MCP_DOWNTIME_THRESHOLD_MS;
14
+ /**
15
+ * Check if a nudge should be fired for the given trigger type.
16
+ * Returns the nudge text if it should fire, null otherwise.
17
+ */
18
+ checkTriggers(db: Database, triggerType: NudgeTriggerType): string | null;
19
+ /**
20
+ * Record that a nudge has been fired for the given trigger type.
21
+ * This ensures the trigger will never fire again.
22
+ */
23
+ recordNudge(db: Database, triggerType: NudgeTriggerType): void;
24
+ /**
25
+ * Get the nudge text for a given trigger type.
26
+ * Tone: factual, helpful, not pushy. Surfaced through the agent's voice.
27
+ */
28
+ getNudgeText(triggerType: NudgeTriggerType): string;
29
+ /**
30
+ * Check if the 'second_integration' trigger should fire.
31
+ * Counts distinct session_ids in raw_log for this userId.
32
+ * If >1 unique session has ingested data, that's a second integration.
33
+ *
34
+ * Only checks once per session to avoid redundant queries.
35
+ */
36
+ checkSecondIntegration(db: Database, userId: string, currentSessionId: string): string | null;
37
+ /**
38
+ * Trigger the MCP downtime nudge.
39
+ * Called when MCP client connection fails.
40
+ * Tracks downtime duration in-memory; fires if >5 minutes.
41
+ */
42
+ triggerMcpDowntime(db: Database): string | null;
43
+ /**
44
+ * Reset MCP downtime tracking (call when connection is restored).
45
+ */
46
+ resetMcpDowntime(): void;
47
+ }
48
+ export {};
49
+ //# sourceMappingURL=nudge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nudge.d.ts","sourceRoot":"","sources":["../src/nudge.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GAAG,cAAc,CAAC;AAIrE,KAAK,QAAQ,GAAG,GAAG,CAAC;AAEpB;;;GAGG;AACH,qBAAa,YAAY;IACvB,iFAAiF;IACjF,OAAO,CAAC,YAAY,CAA0B;IAE9C,oEAAoE;IACpE,OAAO,CAAC,gBAAgB,CAAuB;IAE/C,qEAAqE;IACrE,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAiB;IAE3D;;;OAGG;IACH,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,gBAAgB,GAAG,MAAM,GAAG,IAAI;IAezE;;;OAGG;IACH,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,gBAAgB,GAAG,IAAI;IAO9D;;;OAGG;IACH,YAAY,CAAC,WAAW,EAAE,gBAAgB,GAAG,MAAM;IAWnD;;;;;;OAMG;IACH,sBAAsB,CACpB,EAAE,EAAE,QAAQ,EACZ,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,GACvB,MAAM,GAAG,IAAI;IAsBhB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI;IAmB/C;;OAEG;IACH,gBAAgB,IAAI,IAAI;CAGzB"}
package/dist/nudge.js ADDED
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Manages one-time contextual upgrade nudges to the user.
3
+ * Each trigger type fires exactly once per install, tracked in the nudge_log table.
4
+ */
5
+ export class NudgeManager {
6
+ /** Track which sessions have been seen this runtime to avoid redundant checks */
7
+ seenSessions = new Set();
8
+ /** Track MCP downtime start timestamp (in-memory, not persisted) */
9
+ mcpDowntimeStart = null;
10
+ /** Threshold in milliseconds for MCP downtime trigger (5 minutes) */
11
+ MCP_DOWNTIME_THRESHOLD_MS = 5 * 60 * 1000;
12
+ /**
13
+ * Check if a nudge should be fired for the given trigger type.
14
+ * Returns the nudge text if it should fire, null otherwise.
15
+ */
16
+ checkTriggers(db, triggerType) {
17
+ // Check if this trigger has already fired (check nudge_log table)
18
+ const existing = db.prepare(`SELECT id FROM nudge_log WHERE trigger_type = ? LIMIT 1`).get(triggerType);
19
+ if (existing) {
20
+ // This trigger has already fired — never fire again
21
+ return null;
22
+ }
23
+ // Trigger has not fired yet — return the nudge text
24
+ return this.getNudgeText(triggerType);
25
+ }
26
+ /**
27
+ * Record that a nudge has been fired for the given trigger type.
28
+ * This ensures the trigger will never fire again.
29
+ */
30
+ recordNudge(db, triggerType) {
31
+ const id = crypto.randomUUID();
32
+ db.prepare(`INSERT INTO nudge_log (id, trigger_type, fired_at) VALUES (?, ?, ?)`).run(id, triggerType, new Date().toISOString());
33
+ }
34
+ /**
35
+ * Get the nudge text for a given trigger type.
36
+ * Tone: factual, helpful, not pushy. Surfaced through the agent's voice.
37
+ */
38
+ getNudgeText(triggerType) {
39
+ switch (triggerType) {
40
+ case 'second_integration':
41
+ return "You've connected multiple integrations - if you want memory to sync across all of them automatically, Plumb hosted handles that: https://getplumb.dev/upgrade";
42
+ case 'mcp_downtime':
43
+ return 'Your local MCP server has been unreachable for a while - Plumb hosted runs in the cloud and stays available 24/7: https://getplumb.dev/upgrade';
44
+ default:
45
+ return '';
46
+ }
47
+ }
48
+ /**
49
+ * Check if the 'second_integration' trigger should fire.
50
+ * Counts distinct session_ids in raw_log for this userId.
51
+ * If >1 unique session has ingested data, that's a second integration.
52
+ *
53
+ * Only checks once per session to avoid redundant queries.
54
+ */
55
+ checkSecondIntegration(db, userId, currentSessionId) {
56
+ // Only check once per session
57
+ if (this.seenSessions.has(currentSessionId)) {
58
+ return null;
59
+ }
60
+ this.seenSessions.add(currentSessionId);
61
+ // Count distinct session_ids in raw_log for this user
62
+ const result = db.prepare(`SELECT COUNT(DISTINCT session_id) as count FROM raw_log WHERE user_id = ?`).get(userId);
63
+ const sessionCount = result?.count ?? 0;
64
+ if (sessionCount > 1) {
65
+ // Multiple sessions detected — check if we should fire the nudge
66
+ return this.checkTriggers(db, 'second_integration');
67
+ }
68
+ return null;
69
+ }
70
+ /**
71
+ * Trigger the MCP downtime nudge.
72
+ * Called when MCP client connection fails.
73
+ * Tracks downtime duration in-memory; fires if >5 minutes.
74
+ */
75
+ triggerMcpDowntime(db) {
76
+ const now = Date.now();
77
+ // Start tracking downtime if not already started
78
+ if (this.mcpDowntimeStart === null) {
79
+ this.mcpDowntimeStart = now;
80
+ return null;
81
+ }
82
+ // Check if downtime exceeds threshold
83
+ const downtimeDuration = now - this.mcpDowntimeStart;
84
+ if (downtimeDuration >= this.MCP_DOWNTIME_THRESHOLD_MS) {
85
+ // Threshold exceeded — check if we should fire the nudge
86
+ return this.checkTriggers(db, 'mcp_downtime');
87
+ }
88
+ return null;
89
+ }
90
+ /**
91
+ * Reset MCP downtime tracking (call when connection is restored).
92
+ */
93
+ resetMcpDowntime() {
94
+ this.mcpDowntimeStart = null;
95
+ }
96
+ }
97
+ //# sourceMappingURL=nudge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nudge.js","sourceRoot":"","sources":["../src/nudge.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,MAAM,OAAO,YAAY;IACvB,iFAAiF;IACzE,YAAY,GAAgB,IAAI,GAAG,EAAE,CAAC;IAE9C,oEAAoE;IAC5D,gBAAgB,GAAkB,IAAI,CAAC;IAE/C,qEAAqE;IACpD,yBAAyB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IAE3D;;;OAGG;IACH,aAAa,CAAC,EAAY,EAAE,WAA6B;QACvD,kEAAkE;QAClE,MAAM,QAAQ,GAAG,EAAE,CAAC,OAAO,CACzB,yDAAyD,CAC1D,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAEnB,IAAI,QAAQ,EAAE,CAAC;YACb,oDAAoD;YACpD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,oDAAoD;QACpD,OAAO,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;IACxC,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,EAAY,EAAE,WAA6B;QACrD,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QAC/B,EAAE,CAAC,OAAO,CACR,qEAAqE,CACtE,CAAC,GAAG,CAAC,EAAE,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IACnD,CAAC;IAED;;;OAGG;IACH,YAAY,CAAC,WAA6B;QACxC,QAAQ,WAAW,EAAE,CAAC;YACpB,KAAK,oBAAoB;gBACvB,OAAO,+JAA+J,CAAC;YACzK,KAAK,cAAc;gBACjB,OAAO,gJAAgJ,CAAC;YAC1J;gBACE,OAAO,EAAE,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,sBAAsB,CACpB,EAAY,EACZ,MAAc,EACd,gBAAwB;QAExB,8BAA8B;QAC9B,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC5C,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAExC,sDAAsD;QACtD,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CACvB,2EAA2E,CAC5E,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAEd,MAAM,YAAY,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC,CAAC;QAExC,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;YACrB,iEAAiE;YACjE,OAAO,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,oBAAoB,CAAC,CAAC;QACtD,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,kBAAkB,CAAC,EAAY;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,iDAAiD;QACjD,IAAI,IAAI,CAAC,gBAAgB,KAAK,IAAI,EAAE,CAAC;YACnC,IAAI,CAAC,gBAAgB,GAAG,GAAG,CAAC;YAC5B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,sCAAsC;QACtC,MAAM,gBAAgB,GAAG,GAAG,GAAG,IAAI,CAAC,gBAAgB,CAAC;QACrD,IAAI,gBAAgB,IAAI,IAAI,CAAC,yBAAyB,EAAE,CAAC;YACvD,yDAAyD;YACzD,OAAO,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC;QAChD,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,gBAAgB;QACd,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAC/B,CAAC;CACF"}
@@ -0,0 +1,37 @@
1
+ type PluginLogger = {
2
+ debug?: (message: string) => void;
3
+ info: (message: string) => void;
4
+ warn: (message: string) => void;
5
+ error: (message: string) => void;
6
+ };
7
+ type PluginHookHandlerMap = {
8
+ llm_output: (event: any, ctx: any) => Promise<void> | void;
9
+ session_end: (event: any, ctx: any) => Promise<void> | void;
10
+ before_prompt_build: (event: any, ctx: any) => Promise<any> | void;
11
+ [key: string]: any;
12
+ };
13
+ type OpenClawPluginApi = {
14
+ id: string;
15
+ name: string;
16
+ pluginConfig?: Record<string, unknown>;
17
+ logger: PluginLogger;
18
+ on: <K extends keyof PluginHookHandlerMap>(hookName: K, handler: PluginHookHandlerMap[K], opts?: {
19
+ priority?: number;
20
+ }) => void;
21
+ };
22
+ type OpenClawPluginDefinition = {
23
+ id?: string;
24
+ name?: string;
25
+ version?: string;
26
+ kind?: 'memory';
27
+ activate?: (api: OpenClawPluginApi) => void | Promise<void>;
28
+ };
29
+ /**
30
+ * Plumb OpenClaw plugin entry point.
31
+ *
32
+ * Automatically ingests every LLM exchange into the local memory store via the
33
+ * llm_output hook. Runs fire-and-forget so it never blocks the agent pipeline.
34
+ */
35
+ export declare const plugin: OpenClawPluginDefinition;
36
+ export {};
37
+ //# sourceMappingURL=plugin-module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-module.d.ts","sourceRoot":"","sources":["../src/plugin-module.ts"],"names":[],"mappings":"AAQA,KAAK,YAAY,GAAG;IAClB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,UAAU,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3D,WAAW,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5D,mBAAmB,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IACnE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB,CAAC;AAEF,KAAK,iBAAiB,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,MAAM,EAAE,YAAY,CAAC;IACrB,EAAE,EAAE,CAAC,CAAC,SAAS,MAAM,oBAAoB,EACvC,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,EAChC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,KACzB,IAAI,CAAC;CACX,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D,CAAC;AAIF;;;;;GAKG;AACH,eAAO,MAAM,MAAM,EAAE,wBAoCpB,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { LocalStore } from '@getplumb/core';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { createPostExchangeHook } from './hooks/post-exchange.js';
5
+ import { createPreResponseHook } from './hooks/pre-response.js';
6
+ import { NudgeManager } from './nudge.js';
7
+ const DEFAULT_DB_PATH = join(homedir(), '.plumb', 'memory.db');
8
+ /**
9
+ * Plumb OpenClaw plugin entry point.
10
+ *
11
+ * Automatically ingests every LLM exchange into the local memory store via the
12
+ * llm_output hook. Runs fire-and-forget so it never blocks the agent pipeline.
13
+ */
14
+ export const plugin = {
15
+ id: 'plumb',
16
+ name: 'Plumb Memory',
17
+ version: '0.1.0',
18
+ kind: 'memory',
19
+ async activate(api) {
20
+ const dbPath = api.pluginConfig?.dbPath ?? DEFAULT_DB_PATH;
21
+ const userId = api.pluginConfig?.userId ?? 'default';
22
+ const shadowMode = api.pluginConfig?.shadowMode ?? false;
23
+ api.logger.info(`[plumb] Activating with dbPath=${dbPath}, userId=${userId}, shadowMode=${shadowMode}`);
24
+ const store = new LocalStore({ dbPath, userId });
25
+ const nudgeManager = new NudgeManager();
26
+ // Register the llm_output hook for auto-ingest
27
+ api.on('llm_output', createPostExchangeHook(store, userId));
28
+ // Register the before_prompt_build hook for memory injection
29
+ api.on('before_prompt_build', createPreResponseHook(store, nudgeManager, shadowMode));
30
+ // Clean up on session end
31
+ api.on('session_end', async () => {
32
+ try {
33
+ store.close();
34
+ api.logger.debug?.('[plumb] Store closed on session_end');
35
+ }
36
+ catch (e) {
37
+ api.logger.debug?.(`[plumb] Error closing store: ${e}`);
38
+ }
39
+ });
40
+ api.logger.info('[plumb] Plugin activated');
41
+ },
42
+ };
43
+ //# sourceMappingURL=plugin-module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-module.js","sourceRoot":"","sources":["../src/plugin-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAqC1C,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAA6B;IAC9C,EAAE,EAAE,OAAO;IACX,IAAI,EAAE,cAAc;IACpB,OAAO,EAAE,OAAO;IAChB,IAAI,EAAE,QAAQ;IAEd,KAAK,CAAC,QAAQ,CAAC,GAAsB;QACnC,MAAM,MAAM,GAAI,GAAG,CAAC,YAAY,EAAE,MAA6B,IAAI,eAAe,CAAC;QACnF,MAAM,MAAM,GAAI,GAAG,CAAC,YAAY,EAAE,MAA6B,IAAI,SAAS,CAAC;QAC7E,MAAM,UAAU,GAAI,GAAG,CAAC,YAAY,EAAE,UAAkC,IAAI,KAAK,CAAC;QAElF,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,kCAAkC,MAAM,YAAY,MAAM,gBAAgB,UAAU,EAAE,CACvF,CAAC;QAEF,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACjD,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC;QAExC,+CAA+C;QAC/C,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,sBAAsB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;QAE5D,6DAA6D;QAC7D,GAAG,CAAC,EAAE,CAAC,qBAAqB,EAAE,qBAAqB,CAAC,KAAK,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC;QAEtF,0BAA0B;QAC1B,GAAG,CAAC,EAAE,CAAC,aAAa,EAAE,KAAK,IAAI,EAAE;YAC/B,IAAI,CAAC;gBACH,KAAK,CAAC,KAAK,EAAE,CAAC;gBACd,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,qCAAqC,CAAC,CAAC;YAC5D,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,gCAAgC,CAAC,EAAE,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IAC9C,CAAC;CACF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@getplumb/openclaw-plugin",
3
+ "version": "0.1.0",
4
+ "description": "Plumb OpenClaw plugin — auto-ingest and memory injection for OpenClaw",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "LICENSE",
12
+ "README.md"
13
+ ],
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "exports": {
18
+ ".": {
19
+ "import": "./dist/index.js",
20
+ "types": "./dist/index.d.ts"
21
+ }
22
+ },
23
+ "dependencies": {
24
+ "@modelcontextprotocol/sdk": "^1.27.1",
25
+ "@getplumb/core": "0.1.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/better-sqlite3": "^7.6.13",
29
+ "better-sqlite3": "^12.6.2",
30
+ "openclaw": "^2026.3.2",
31
+ "typescript": "^5.4.0",
32
+ "vitest": "^4.0.18"
33
+ },
34
+ "scripts": {
35
+ "build": "tsc",
36
+ "test": "vitest run",
37
+ "lint": "echo \"No lint yet\" && exit 0",
38
+ "clean": "rm -rf dist *.tsbuildinfo"
39
+ }
40
+ }