openclaw-penfield 1.1.2 → 2.0.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/README.md CHANGED
@@ -38,7 +38,7 @@ Penfield is in **free beta**. Sign up for access:
38
38
 
39
39
  Native OpenClaw plugin providing direct integration with Penfield's memory and knowledge graph API. This plugin offers 4-5x performance improvement over the MCP server approach by eliminating the mcporter → MCP → Penfield stack.
40
40
 
41
- - **16 Memory Tools**
41
+ - **17 Memory Tools**
42
42
  - **OAuth 2.1 Device Code Flow**: Secure authentication following RFC 8628
43
43
  - **Hybrid Search**: BM25 + vector + graph search capabilities
44
44
  - **Knowledge Graph**: Build and traverse relationships between memories
@@ -66,6 +66,20 @@ openclaw plugins install -l .
66
66
 
67
67
  The plugin is **auto-enabled when loaded**. No configuration required for basic use.
68
68
 
69
+ ### Plugin Allowlist
70
+
71
+ OpenClaw recommends explicitly trusting non-bundled plugins. If you see `plugins.allow is empty` warnings on startup, add the plugin to your allowlist in `openclaw.json`:
72
+
73
+ ```json
74
+ {
75
+ "plugins": {
76
+ "allow": ["openclaw-penfield"]
77
+ }
78
+ }
79
+ ```
80
+
81
+ Once the `allow` array exists, future `openclaw plugins install` and `openclaw plugins enable` commands will automatically append to it.
82
+
69
83
  ### Plugin Config
70
84
 
71
85
  In `openclaw.json` under `plugins.entries`:
@@ -175,7 +189,7 @@ Store a new memory in Penfield.
175
189
  ```
176
190
 
177
191
  #### `penfield_recall`
178
- Hybrid search using BM25 + vector + graph.
192
+ Hybrid search using BM25 + vector + graph. Returns compact responses (essential fields only).
179
193
 
180
194
  **Parameters:**
181
195
  - `query` (required): Search query (1-4,000 chars)
@@ -186,6 +200,10 @@ Hybrid search using BM25 + vector + graph.
186
200
  - `memory_types` (optional): Filter by types
187
201
  - `importance_threshold` (optional): Minimum importance
188
202
  - `enable_graph_expansion` (optional): Enable traversal (default: true)
203
+ - `start_date` (optional): Filter memories created on or after this date (ISO 8601, e.g. "2025-01-01")
204
+ - `end_date` (optional): Filter memories created on or before this date (ISO 8601, e.g. "2025-12-31")
205
+ - `sort` (optional): Sort order — "relevance" (default), "created_desc", or "created_asc"
206
+ - `max_content_length` (optional): Truncate content to N characters (50-10,000). Full content available via `penfield_fetch`.
189
207
 
190
208
  **Example:**
191
209
  ```typescript
@@ -194,18 +212,23 @@ Hybrid search using BM25 + vector + graph.
194
212
  "limit": 10,
195
213
  "vector_weight": 0.5,
196
214
  "bm25_weight": 0.3,
197
- "graph_weight": 0.2
215
+ "graph_weight": 0.2,
216
+ "start_date": "2025-06-01"
198
217
  }
199
218
  ```
200
219
 
201
220
  #### `penfield_search`
202
- Semantic search variant (higher vector weight).
221
+ Semantic search variant (higher vector weight). Returns compact responses (essential fields only).
203
222
 
204
223
  **Parameters:**
205
- - `query` (required): Search query
206
- - `limit` (optional): Max results
224
+ - `query` (required): Search query (1-4,000 chars)
225
+ - `limit` (optional): Max results (default: 20, max: 100)
207
226
  - `memory_types` (optional): Filter by types
208
227
  - `importance_threshold` (optional): Minimum importance
228
+ - `start_date` (optional): Filter memories created on or after this date (ISO 8601)
229
+ - `end_date` (optional): Filter memories created on or before this date (ISO 8601)
230
+ - `sort` (optional): Sort order — "relevance" (default), "created_desc", or "created_asc"
231
+ - `max_content_length` (optional): Truncate content to N characters (50-10,000)
209
232
 
210
233
  #### `penfield_fetch`
211
234
  Get a specific memory by ID.
@@ -253,6 +276,21 @@ Create a relationship between two memories.
253
276
  }
254
277
  ```
255
278
 
279
+ #### `penfield_disconnect`
280
+ Remove a relationship between two memories.
281
+
282
+ **Parameters:**
283
+ - `from_memory_id` (required): Source memory ID (UUID format)
284
+ - `to_memory_id` (required): Target memory ID (UUID format)
285
+
286
+ **Example:**
287
+ ```typescript
288
+ {
289
+ "from_memory_id": "22618318-8d82-49c9-8bb8-1cf3a61b3c75",
290
+ "to_memory_id": "20413926-2446-4f88-bfd6-749b37969f34"
291
+ }
292
+ ```
293
+
256
294
  #### `penfield_explore`
257
295
  Traverse the knowledge graph from a starting memory.
258
296
 
@@ -478,18 +516,20 @@ src/
478
516
  ├── hooks.ts # Lifecycle hooks (auto-awaken, auto-orient, flush config check)
479
517
  ├── auth-service.ts # Background OAuth token refresh service
480
518
  ├── api-client.ts # HTTP client wrapper
519
+ ├── response-compact.ts # Response compaction (mirrors MCP field-stripping)
481
520
  ├── runtime.ts # Runtime factory (receives authService from index.ts)
482
521
  ├── store.ts # Credential file I/O with TOKEN_EXPIRY_BUFFER_MS
483
522
  ├── cli.ts # CLI command registration (penfield login)
484
523
  ├── device-flow.ts # RFC 8628 Device Code Flow implementation
485
524
  └── tools/
486
- ├── index.ts # Tool registry (16 tools)
525
+ ├── index.ts # Tool registry (17 tools)
487
526
  ├── store.ts # penfield_store
488
527
  ├── recall.ts # penfield_recall
489
528
  ├── search.ts # penfield_search
490
529
  ├── fetch.ts # penfield_fetch
491
530
  ├── update-memory.ts # penfield_update_memory
492
531
  ├── connect.ts # penfield_connect
532
+ ├── disconnect.ts # penfield_disconnect
493
533
  ├── explore.ts # penfield_explore
494
534
  ├── save-context.ts # penfield_save_context
495
535
  ├── restore-context.ts # penfield_restore_context
@@ -525,6 +565,7 @@ The plugin uses two services and one hook registered with OpenClaw:
525
565
  |------|--------|----------|
526
566
  | awaken | GET | /api/v2/personality/awakening |
527
567
  | connect | POST | /api/v2/relationships |
568
+ | disconnect | DELETE | /api/v2/relationships/between |
528
569
  | delete_artifact | DELETE | /api/v2/artifacts |
529
570
  | explore | POST | /api/v2/relationships/traverse |
530
571
  | fetch | GET | /api/v2/memories/{id} |
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { createAuthService } from "./src/auth-service.js";
7
7
  const penfieldPlugin = {
8
8
  id: "openclaw-penfield",
9
9
  name: "Penfield Memory",
10
- description: "Native Penfield memory integration with 16 tools for knowledge management",
10
+ description: "Native Penfield memory integration with 17 tools for knowledge management",
11
11
  configSchema: {
12
12
  parse(value) {
13
13
  return PenfieldConfigSchema.parse(value ?? {});
@@ -79,7 +79,7 @@ const penfieldPlugin = {
79
79
  logger.info("[penfield-auth] Service stopped");
80
80
  },
81
81
  });
82
- // Register all 16 tools
82
+ // Register all 17 tools
83
83
  registerPenfieldTools(api, ensureRuntime);
84
84
  // Register lifecycle hooks (auto-awaken + auto-orient, injected every turn)
85
85
  registerPenfieldHooks({ api, config: cfg, ensureRuntime, logger });
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/api-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM,CAAC;gBAFP,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,YAAY,YAAA;IAGzB,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,OAAO,EACd,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACnC,OAAO,CAAC,CAAC,CAAC;IAuDP,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAI1E,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIrD,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIpD,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAGpF"}
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/api-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM,CAAC;gBAFP,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,YAAY,YAAA;IAGzB,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,OAAO,EACd,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACnC,OAAO,CAAC,CAAC,CAAC;IA2DP,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAI1E,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIrD,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIpD,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAGpF"}
@@ -51,6 +51,9 @@ export class PenfieldApiClient {
51
51
  this.logger?.error(`[penfield] ${errorMessage}`);
52
52
  throw new Error(errorMessage);
53
53
  }
54
+ if (response.status === 204) {
55
+ return {};
56
+ }
54
57
  const data = await response.json();
55
58
  return (data.data || data);
56
59
  }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Response compaction utilities.
3
+ *
4
+ * Mirrors the MCP server's field-stripping patterns so models receive only
5
+ * the fields they need, saving context tokens and reducing noise.
6
+ */
7
+ /** Minimum relevance score — results below this are dropped (matches MCP) */
8
+ export declare const RELEVANCE_THRESHOLD = 0.05;
9
+ interface CompactMemory {
10
+ id: string | undefined;
11
+ content: string | undefined;
12
+ type: string;
13
+ relevance: number;
14
+ created: string | undefined;
15
+ tags: string[];
16
+ source_type?: string;
17
+ filename?: string;
18
+ document_title?: string;
19
+ document_id?: string;
20
+ content_with_context?: string;
21
+ chunk_index?: number;
22
+ total_chunks?: number;
23
+ }
24
+ export interface CompactRecallResponse {
25
+ query: string;
26
+ found: number;
27
+ memories: CompactMemory[];
28
+ }
29
+ /**
30
+ * Compact a hybrid search response to essential fields.
31
+ *
32
+ * Strips: score_breakdown, search_metadata, knowledge_cloud, analyzed_query,
33
+ * raw metadata objects. Filters results below RELEVANCE_THRESHOLD.
34
+ *
35
+ * Optionally sorts by created date and truncates content.
36
+ */
37
+ export declare function compactRecallResponse(response: any, query: string, options?: {
38
+ sort?: string;
39
+ maxContentLength?: number;
40
+ }): CompactRecallResponse;
41
+ interface CompactReflectMemory {
42
+ content: string | undefined;
43
+ type: string;
44
+ importance: number | undefined;
45
+ score: number | undefined;
46
+ }
47
+ export interface CompactReflectResponse {
48
+ time_window: string;
49
+ memories_analyzed: number;
50
+ active_topics: unknown[];
51
+ top_memories: CompactReflectMemory[];
52
+ patterns: unknown[];
53
+ insights: unknown[];
54
+ }
55
+ /**
56
+ * Compact a reflect response to essential fields.
57
+ *
58
+ * Strips full statistics object. Slices memories to top 5, topics to top 10.
59
+ */
60
+ export declare function compactReflectResponse(response: any, timeWindow: string): CompactReflectResponse;
61
+ interface CompactNode {
62
+ id: string;
63
+ preview?: string;
64
+ type?: string;
65
+ tags?: string[];
66
+ }
67
+ interface CompactRelationship {
68
+ id: string;
69
+ type?: string;
70
+ strength?: number;
71
+ }
72
+ interface CompactPath {
73
+ nodes: CompactNode[];
74
+ relationships: CompactRelationship[];
75
+ depth: number;
76
+ }
77
+ export interface CompactExploreResponse {
78
+ start_memory: string;
79
+ paths_found: number;
80
+ max_depth_reached: number;
81
+ paths: CompactPath[];
82
+ message?: string;
83
+ }
84
+ /**
85
+ * Compact a graph traversal response.
86
+ *
87
+ * Enriches nodes/relationships with selective fields from detail lookups.
88
+ */
89
+ export declare function compactExploreResponse(response: any, startMemoryId: string): CompactExploreResponse;
90
+ export {};
91
+ //# sourceMappingURL=response-compact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-compact.d.ts","sourceRoot":"","sources":["../../src/response-compact.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,6EAA6E;AAC7E,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAyBxC,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAEnC,QAAQ,EAAE,GAAG,EACb,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GACrD,qBAAqB,CA0DvB;AAMD,UAAU,oBAAoB;IAC5B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,OAAO,EAAE,CAAC;IACzB,YAAY,EAAE,oBAAoB,EAAE,CAAC;IACrC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAEpC,QAAQ,EAAE,GAAG,EACb,UAAU,EAAE,MAAM,GACjB,sBAAsB,CAmBxB;AAMD,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,UAAU,mBAAmB;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,WAAW;IACnB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,aAAa,EAAE,mBAAmB,EAAE,CAAC;IACrC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAEpC,QAAQ,EAAE,GAAG,EACb,aAAa,EAAE,MAAM,GACpB,sBAAsB,CAiDxB"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Response compaction utilities.
3
+ *
4
+ * Mirrors the MCP server's field-stripping patterns so models receive only
5
+ * the fields they need, saving context tokens and reducing noise.
6
+ */
7
+ /** Minimum relevance score — results below this are dropped (matches MCP) */
8
+ export const RELEVANCE_THRESHOLD = 0.05;
9
+ /** Maximum memories returned in reflect compaction */
10
+ const MAX_REFLECT_MEMORIES = 5;
11
+ /** Maximum active topics returned in reflect compaction */
12
+ const MAX_REFLECT_TOPICS = 10;
13
+ /**
14
+ * Compact a hybrid search response to essential fields.
15
+ *
16
+ * Strips: score_breakdown, search_metadata, knowledge_cloud, analyzed_query,
17
+ * raw metadata objects. Filters results below RELEVANCE_THRESHOLD.
18
+ *
19
+ * Optionally sorts by created date and truncates content.
20
+ */
21
+ export function compactRecallResponse(
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- raw API shape
23
+ response, query, options) {
24
+ const items = response?.items ?? [];
25
+ const memories = [];
26
+ for (const result of items) {
27
+ const score = result.score ?? 0;
28
+ if (score < RELEVANCE_THRESHOLD)
29
+ continue;
30
+ const metadata = result.metadata ?? {};
31
+ const memoryType = metadata.memory_type ?? "unknown";
32
+ const sourceType = result.source_type ?? metadata.source_type;
33
+ const entry = {
34
+ id: result.id,
35
+ content: result.content,
36
+ type: memoryType,
37
+ relevance: score,
38
+ created: result.created_at,
39
+ tags: result.tags ?? [],
40
+ };
41
+ // Include document metadata when applicable
42
+ if (sourceType === "document_upload") {
43
+ entry.source_type = "document";
44
+ if (metadata.filename)
45
+ entry.filename = metadata.filename;
46
+ if (metadata.document_title)
47
+ entry.document_title = metadata.document_title;
48
+ if (metadata.document_id)
49
+ entry.document_id = metadata.document_id;
50
+ }
51
+ // Include chunk context when present
52
+ if (result.content_with_context) {
53
+ entry.content_with_context = result.content_with_context;
54
+ entry.chunk_index = result.chunk_index;
55
+ entry.total_chunks = result.total_chunks;
56
+ }
57
+ memories.push(entry);
58
+ }
59
+ // Client-side sort
60
+ if (options?.sort === "created_desc") {
61
+ memories.sort((a, b) => (b.created ?? "").localeCompare(a.created ?? ""));
62
+ }
63
+ else if (options?.sort === "created_asc") {
64
+ memories.sort((a, b) => (a.created ?? "").localeCompare(b.created ?? ""));
65
+ }
66
+ // default "relevance" — already sorted by API
67
+ // Content truncation
68
+ if (options?.maxContentLength != null && options.maxContentLength > 0) {
69
+ const max = options.maxContentLength;
70
+ for (const m of memories) {
71
+ if (m.content && m.content.length > max) {
72
+ m.content = m.content.slice(0, max) + "... [truncated, use penfield_fetch for full content]";
73
+ }
74
+ }
75
+ }
76
+ return { query, found: memories.length, memories };
77
+ }
78
+ /**
79
+ * Compact a reflect response to essential fields.
80
+ *
81
+ * Strips full statistics object. Slices memories to top 5, topics to top 10.
82
+ */
83
+ export function compactReflectResponse(
84
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- raw API shape
85
+ response, timeWindow) {
86
+ const rawMemories = response?.memories ?? [];
87
+ const topMemories = rawMemories.slice(0, MAX_REFLECT_MEMORIES).map((m) => ({
88
+ content: m.content,
89
+ type: m.memory_type ?? "unknown",
90
+ importance: m.importance,
91
+ score: m.score,
92
+ }));
93
+ const activeTopics = (response?.active_topics ?? []).slice(0, MAX_REFLECT_TOPICS);
94
+ return {
95
+ time_window: timeWindow,
96
+ memories_analyzed: response?.statistics?.total_memories_analyzed ?? 0,
97
+ active_topics: activeTopics,
98
+ top_memories: topMemories,
99
+ patterns: response?.emerging_patterns ?? [],
100
+ insights: response?.relationship_insights ?? [],
101
+ };
102
+ }
103
+ /**
104
+ * Compact a graph traversal response.
105
+ *
106
+ * Enriches nodes/relationships with selective fields from detail lookups.
107
+ */
108
+ export function compactExploreResponse(
109
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- raw API shape
110
+ response, startMemoryId) {
111
+ const nodeDetails = response?.node_details ?? {};
112
+ const relDetails = response?.relationship_details ?? {};
113
+ const rawPaths = response?.paths ?? [];
114
+ const paths = rawPaths.map((path) => {
115
+ const rawNodes = path.nodes ?? [];
116
+ const rawRels = path.relationships ?? [];
117
+ const nodes = rawNodes.map((nid) => {
118
+ const detail = nodeDetails[nid];
119
+ if (detail) {
120
+ return {
121
+ id: nid,
122
+ preview: detail.preview,
123
+ type: detail.type,
124
+ tags: detail.tags ?? [],
125
+ };
126
+ }
127
+ return { id: nid };
128
+ });
129
+ const relationships = rawRels.map((rid) => {
130
+ const detail = relDetails[rid];
131
+ if (detail) {
132
+ return {
133
+ id: rid,
134
+ type: detail.type,
135
+ strength: detail.strength,
136
+ };
137
+ }
138
+ return { id: rid };
139
+ });
140
+ return { nodes, relationships, depth: rawNodes.length - 1 };
141
+ });
142
+ const result = {
143
+ start_memory: startMemoryId,
144
+ paths_found: paths.length,
145
+ max_depth_reached: response?.max_depth_reached ?? 0,
146
+ paths,
147
+ };
148
+ if (paths.length === 0) {
149
+ result.message = "No connections found from this memory";
150
+ }
151
+ return result;
152
+ }
@@ -0,0 +1,7 @@
1
+ import type { PenfieldApiClient } from "../api-client.js";
2
+ export declare const DisconnectToolSchema: import("@sinclair/typebox").TObject<{
3
+ from_memory_id: import("@sinclair/typebox").TString;
4
+ to_memory_id: import("@sinclair/typebox").TString;
5
+ }>;
6
+ export declare function executeDisconnectTool(apiClient: PenfieldApiClient, params: any): Promise<any>;
7
+ //# sourceMappingURL=disconnect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disconnect.d.ts","sourceRoot":"","sources":["../../../src/tools/disconnect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,eAAO,MAAM,oBAAoB;;;EASE,CAAC;AAEpC,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,GAAG,GACV,OAAO,CAAC,GAAG,CAAC,CA4Bd"}
@@ -0,0 +1,38 @@
1
+ import { Type } from "../types/typebox.js";
2
+ import { validateUuid } from "../validation.js";
3
+ export const DisconnectToolSchema = Type.Object({
4
+ from_memory_id: Type.String({
5
+ description: "Source memory ID (UUID format)",
6
+ examples: ["22618318-8d82-49c9-8bb8-1cf3a61b3c75"],
7
+ }),
8
+ to_memory_id: Type.String({
9
+ description: "Target memory ID (UUID format)",
10
+ examples: ["20413926-2446-4f88-bfd6-749b37969f34"],
11
+ }),
12
+ }, { additionalProperties: false });
13
+ export async function executeDisconnectTool(apiClient, params // eslint-disable-line @typescript-eslint/no-explicit-any -- validated by TypeBox schema
14
+ ) {
15
+ validateUuid(params.from_memory_id, 'from_memory_id');
16
+ validateUuid(params.to_memory_id, 'to_memory_id');
17
+ // Map user-friendly field names to API field names
18
+ const queryParams = {
19
+ from_id: params.from_memory_id,
20
+ to_id: params.to_memory_id,
21
+ };
22
+ await apiClient.delete("/api/v2/relationships/between", queryParams);
23
+ const result = {
24
+ success: true,
25
+ from_id: params.from_memory_id,
26
+ to_id: params.to_memory_id,
27
+ message: "Relationship removed",
28
+ };
29
+ return {
30
+ content: [
31
+ {
32
+ type: "text",
33
+ text: JSON.stringify(result, null, 2),
34
+ },
35
+ ],
36
+ details: result,
37
+ };
38
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"explore.d.ts","sourceRoot":"","sources":["../../../src/tools/explore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,eAAO,MAAM,iBAAiB;;;;;EAwBK,CAAC;AAEpC,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,GAAG,CAAC,CAcd"}
1
+ {"version":3,"file":"explore.d.ts","sourceRoot":"","sources":["../../../src/tools/explore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAI1D,eAAO,MAAM,iBAAiB;;;;;EAwBK,CAAC;AAEpC,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,GAAG,CAAC,CAgBd"}
@@ -1,5 +1,6 @@
1
1
  import { Type, RelationshipTypeSchema } from "../types/typebox.js";
2
2
  import { validateUuid } from "../validation.js";
3
+ import { compactExploreResponse } from "../response-compact.js";
3
4
  export const ExploreToolSchema = Type.Object({
4
5
  start_memory_id: Type.String({
5
6
  description: "Starting memory ID for graph traversal (UUID format)",
@@ -23,11 +24,12 @@ export async function executeExploreTool(apiClient, params) {
23
24
  const { start_memory_id } = params;
24
25
  validateUuid(start_memory_id, 'start_memory_id');
25
26
  const response = await apiClient.post("/api/v2/relationships/traverse", params);
27
+ const compact = compactExploreResponse(response, start_memory_id);
26
28
  return {
27
29
  content: [
28
30
  {
29
31
  type: "text",
30
- text: JSON.stringify(response, null, 2),
32
+ text: JSON.stringify(compact, null, 2),
31
33
  },
32
34
  ],
33
35
  details: response,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AA6BrD,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,iBAAiB,EACtB,aAAa,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,QAgK9C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AA8BrD,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,iBAAiB,EACtB,aAAa,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,QAwK9C"}
@@ -4,6 +4,7 @@ import { SearchToolSchema, executeSearchTool } from "./search.js";
4
4
  import { FetchToolSchema, executeFetchTool } from "./fetch.js";
5
5
  import { UpdateMemoryToolSchema, executeUpdateMemoryTool } from "./update-memory.js";
6
6
  import { ConnectToolSchema, executeConnectTool } from "./connect.js";
7
+ import { DisconnectToolSchema, executeDisconnectTool } from "./disconnect.js";
7
8
  import { ExploreToolSchema, executeExploreTool } from "./explore.js";
8
9
  import { SaveContextToolSchema, executeSaveContextTool } from "./save-context.js";
9
10
  import { RestoreContextToolSchema, executeRestoreContextTool, } from "./restore-context.js";
@@ -38,13 +39,14 @@ export function registerPenfieldTools(api, ensureRuntime) {
38
39
  },
39
40
  });
40
41
  };
41
- // Register all 16 tools
42
+ // Register all 17 tools
42
43
  registerTool("penfield_store", "Store Memory", "Store a new memory in Penfield", StoreToolSchema, executeStoreTool);
43
44
  registerTool("penfield_recall", "Recall Memories", "Search memories using hybrid BM25 + vector + graph search", RecallToolSchema, executeRecallTool);
44
45
  registerTool("penfield_search", "Search Memories", "Semantic search for memories", SearchToolSchema, executeSearchTool);
45
46
  registerTool("penfield_fetch", "Fetch Memory", "Get a specific memory by ID", FetchToolSchema, executeFetchTool);
46
47
  registerTool("penfield_update_memory", "Update Memory", "Update an existing memory", UpdateMemoryToolSchema, executeUpdateMemoryTool);
47
48
  registerTool("penfield_connect", "Connect Memories", "Create a relationship between two memories", ConnectToolSchema, executeConnectTool);
49
+ registerTool("penfield_disconnect", "Disconnect Memories", "Remove a relationship between two memories", DisconnectToolSchema, executeDisconnectTool);
48
50
  registerTool("penfield_explore", "Explore Graph", "Traverse the knowledge graph from a starting memory", ExploreToolSchema, executeExploreTool);
49
51
  registerTool("penfield_save_context", "Save Context", "Save a cognitive state checkpoint with name and description for handoff to another agent or future session", SaveContextToolSchema, executeSaveContextTool);
50
52
  registerTool("penfield_restore_context", "Restore Context", "Restore a previously saved context checkpoint by name, UUID, or 'awakening' for personality briefing", RestoreContextToolSchema, executeRestoreContextTool);
@@ -8,6 +8,10 @@ export declare const RecallToolSchema: import("@sinclair/typebox").TObject<{
8
8
  memory_types: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"fact">, import("@sinclair/typebox").TLiteral<"insight">, import("@sinclair/typebox").TLiteral<"conversation">, import("@sinclair/typebox").TLiteral<"correction">, import("@sinclair/typebox").TLiteral<"reference">, import("@sinclair/typebox").TLiteral<"task">, import("@sinclair/typebox").TLiteral<"checkpoint">, import("@sinclair/typebox").TLiteral<"identity_core">, import("@sinclair/typebox").TLiteral<"personality_trait">, import("@sinclair/typebox").TLiteral<"relationship">, import("@sinclair/typebox").TLiteral<"strategy">]>>>;
9
9
  importance_threshold: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
10
10
  enable_graph_expansion: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
11
+ start_date: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
12
+ end_date: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
13
+ sort: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"relevance">, import("@sinclair/typebox").TLiteral<"created_desc">, import("@sinclair/typebox").TLiteral<"created_asc">]>>;
14
+ max_content_length: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
11
15
  }>;
12
16
  export declare function executeRecallTool(apiClient: PenfieldApiClient, params: any): Promise<any>;
13
17
  //# sourceMappingURL=recall.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../../../src/tools/recall.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,eAAO,MAAM,gBAAgB;;;;;;;;;EAuDM,CAAC;AAEpC,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,GAAG,GACV,OAAO,CAAC,GAAG,CAAC,CAqBd"}
1
+ {"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../../../src/tools/recall.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;EA2DM,CAAC;AAEpC,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,GAAG,GACV,OAAO,CAAC,GAAG,CAAC,CA6Bd"}
@@ -1,4 +1,5 @@
1
- import { Type, MemoryTypeSchema, ImportanceScoreSchema } from "../types/typebox.js";
1
+ import { Type, MemoryTypeSchema, ImportanceScoreSchema, StartDateSchema, EndDateSchema, SortOrderSchema, MaxContentLengthSchema } from "../types/typebox.js";
2
+ import { compactRecallResponse } from "../response-compact.js";
2
3
  export const RecallToolSchema = Type.Object({
3
4
  query: Type.String({
4
5
  description: "Search query (1-4,000 chars)",
@@ -40,6 +41,10 @@ export const RecallToolSchema = Type.Object({
40
41
  description: "Enable graph traversal (default: true)",
41
42
  default: true,
42
43
  })),
44
+ start_date: Type.Optional(StartDateSchema),
45
+ end_date: Type.Optional(EndDateSchema),
46
+ sort: Type.Optional(SortOrderSchema),
47
+ max_content_length: Type.Optional(MaxContentLengthSchema),
43
48
  }, { additionalProperties: false });
44
49
  export async function executeRecallTool(apiClient, params // eslint-disable-line @typescript-eslint/no-explicit-any -- validated by TypeBox schema
45
50
  ) {
@@ -51,12 +56,18 @@ export async function executeRecallTool(apiClient, params // eslint-disable-line
51
56
  if (Math.abs(sum - 1.0) > 0.01) {
52
57
  throw new Error(`Weights must sum to 1.0 (got ${sum})`);
53
58
  }
54
- const response = await apiClient.post("/api/v2/search/hybrid", params);
59
+ // Separate plugin-only params from API params
60
+ const { sort, max_content_length, ...apiParams } = params;
61
+ const response = await apiClient.post("/api/v2/search/hybrid", apiParams);
62
+ const compact = compactRecallResponse(response, params.query, {
63
+ sort,
64
+ maxContentLength: max_content_length,
65
+ });
55
66
  return {
56
67
  content: [
57
68
  {
58
69
  type: "text",
59
- text: JSON.stringify(response, null, 2),
70
+ text: JSON.stringify(compact, null, 2),
60
71
  },
61
72
  ],
62
73
  details: response,
@@ -1 +1 @@
1
- {"version":3,"file":"reflect.d.ts","sourceRoot":"","sources":["../../../src/tools/reflect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,eAAO,MAAM,iBAAiB;;;;;EAwCK,CAAC;AASpC,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,GAAG,CAAC,CA0Bd"}
1
+ {"version":3,"file":"reflect.d.ts","sourceRoot":"","sources":["../../../src/tools/reflect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,eAAO,MAAM,iBAAiB;;;;;EAwCK,CAAC;AASpC,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,GAAG,CAAC,CA8Bd"}
@@ -1,4 +1,5 @@
1
1
  import { Type } from "../types/typebox.js";
2
+ import { compactReflectResponse } from "../response-compact.js";
2
3
  export const ReflectToolSchema = Type.Object({
3
4
  time_window: Type.Optional(Type.Union([
4
5
  Type.Literal("recent"),
@@ -34,6 +35,7 @@ const TIME_WINDOW_ALIASES = {
34
35
  export async function executeReflectTool(apiClient, params) {
35
36
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- params validated by schema
36
37
  const p = { ...params };
38
+ const originalTimeWindow = p.time_window ?? "recent";
37
39
  if (p.time_window === "90d") {
38
40
  // API has no 90d window — convert to explicit date range (matches MCP behavior)
39
41
  if (!p.start_date) {
@@ -47,11 +49,12 @@ export async function executeReflectTool(apiClient, params) {
47
49
  p.time_window = TIME_WINDOW_ALIASES[p.time_window];
48
50
  }
49
51
  const response = await apiClient.post("/api/v2/analysis/reflect", p);
52
+ const compact = compactReflectResponse(response, originalTimeWindow);
50
53
  return {
51
54
  content: [
52
55
  {
53
56
  type: "text",
54
- text: JSON.stringify(response, null, 2),
57
+ text: JSON.stringify(compact, null, 2),
55
58
  },
56
59
  ],
57
60
  details: response,
@@ -4,6 +4,10 @@ export declare const SearchToolSchema: import("@sinclair/typebox").TObject<{
4
4
  limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
5
5
  memory_types: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"fact">, import("@sinclair/typebox").TLiteral<"insight">, import("@sinclair/typebox").TLiteral<"conversation">, import("@sinclair/typebox").TLiteral<"correction">, import("@sinclair/typebox").TLiteral<"reference">, import("@sinclair/typebox").TLiteral<"task">, import("@sinclair/typebox").TLiteral<"checkpoint">, import("@sinclair/typebox").TLiteral<"identity_core">, import("@sinclair/typebox").TLiteral<"personality_trait">, import("@sinclair/typebox").TLiteral<"relationship">, import("@sinclair/typebox").TLiteral<"strategy">]>>>;
6
6
  importance_threshold: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
7
+ start_date: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
8
+ end_date: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
9
+ sort: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"relevance">, import("@sinclair/typebox").TLiteral<"created_desc">, import("@sinclair/typebox").TLiteral<"created_asc">]>>;
10
+ max_content_length: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
7
11
  }>;
8
12
  export declare function executeSearchTool(apiClient: PenfieldApiClient, params: any): Promise<any>;
9
13
  //# sourceMappingURL=search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/tools/search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,eAAO,MAAM,gBAAgB;;;;;EAyBM,CAAC;AAEpC,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,GAAG,GACV,OAAO,CAAC,GAAG,CAAC,CAmBd"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/tools/search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,eAAO,MAAM,gBAAgB;;;;;;;;;EA6BM,CAAC;AAEpC,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,GAAG,GACV,OAAO,CAAC,GAAG,CAAC,CA2Bd"}
@@ -1,4 +1,5 @@
1
- import { Type, MemoryTypeSchema, ImportanceScoreSchema } from "../types/typebox.js";
1
+ import { Type, MemoryTypeSchema, ImportanceScoreSchema, StartDateSchema, EndDateSchema, SortOrderSchema, MaxContentLengthSchema } from "../types/typebox.js";
2
+ import { compactRecallResponse } from "../response-compact.js";
2
3
  export const SearchToolSchema = Type.Object({
3
4
  query: Type.String({
4
5
  description: "Search query (1-4,000 chars)",
@@ -18,22 +19,32 @@ export const SearchToolSchema = Type.Object({
18
19
  ...ImportanceScoreSchema,
19
20
  description: "Minimum importance (0-1)",
20
21
  })),
22
+ start_date: Type.Optional(StartDateSchema),
23
+ end_date: Type.Optional(EndDateSchema),
24
+ sort: Type.Optional(SortOrderSchema),
25
+ max_content_length: Type.Optional(MaxContentLengthSchema),
21
26
  }, { additionalProperties: false });
22
27
  export async function executeSearchTool(apiClient, params // eslint-disable-line @typescript-eslint/no-explicit-any -- validated by TypeBox schema
23
28
  ) {
29
+ // Separate plugin-only params from API params
30
+ const { sort, max_content_length, ...userParams } = params;
24
31
  // Search is a variant of hybrid search with higher vector weight
25
32
  const searchParams = {
26
- ...params,
33
+ ...userParams,
27
34
  vector_weight: 0.6,
28
35
  bm25_weight: 0.3,
29
36
  graph_weight: 0.1,
30
37
  };
31
38
  const response = await apiClient.post("/api/v2/search/hybrid", searchParams);
39
+ const compact = compactRecallResponse(response, params.query, {
40
+ sort,
41
+ maxContentLength: max_content_length,
42
+ });
32
43
  return {
33
44
  content: [
34
45
  {
35
46
  type: "text",
36
- text: JSON.stringify(response, null, 2),
47
+ text: JSON.stringify(compact, null, 2),
37
48
  },
38
49
  ],
39
50
  details: response,
@@ -15,5 +15,9 @@ export declare const ImportanceScoreSchema: import("@sinclair/typebox").TNumber;
15
15
  export declare const ConfidenceScoreSchema: import("@sinclair/typebox").TNumber;
16
16
  export declare const TagsSchema: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
17
17
  export declare const MemoryTypeSchema: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"fact">, import("@sinclair/typebox").TLiteral<"insight">, import("@sinclair/typebox").TLiteral<"conversation">, import("@sinclair/typebox").TLiteral<"correction">, import("@sinclair/typebox").TLiteral<"reference">, import("@sinclair/typebox").TLiteral<"task">, import("@sinclair/typebox").TLiteral<"checkpoint">, import("@sinclair/typebox").TLiteral<"identity_core">, import("@sinclair/typebox").TLiteral<"personality_trait">, import("@sinclair/typebox").TLiteral<"relationship">, import("@sinclair/typebox").TLiteral<"strategy">]>;
18
+ export declare const StartDateSchema: import("@sinclair/typebox").TString;
19
+ export declare const EndDateSchema: import("@sinclair/typebox").TString;
20
+ export declare const SortOrderSchema: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"relevance">, import("@sinclair/typebox").TLiteral<"created_desc">, import("@sinclair/typebox").TLiteral<"created_asc">]>;
21
+ export declare const MaxContentLengthSchema: import("@sinclair/typebox").TNumber;
18
22
  export declare const RelationshipTypeSchema: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"supersedes">, import("@sinclair/typebox").TLiteral<"updates">, import("@sinclair/typebox").TLiteral<"evolution_of">, import("@sinclair/typebox").TLiteral<"supports">, import("@sinclair/typebox").TLiteral<"contradicts">, import("@sinclair/typebox").TLiteral<"disputes">, import("@sinclair/typebox").TLiteral<"parent_of">, import("@sinclair/typebox").TLiteral<"child_of">, import("@sinclair/typebox").TLiteral<"sibling_of">, import("@sinclair/typebox").TLiteral<"causes">, import("@sinclair/typebox").TLiteral<"influenced_by">, import("@sinclair/typebox").TLiteral<"prerequisite_for">, import("@sinclair/typebox").TLiteral<"implements">, import("@sinclair/typebox").TLiteral<"documents">, import("@sinclair/typebox").TLiteral<"example_of">, import("@sinclair/typebox").TLiteral<"tests">, import("@sinclair/typebox").TLiteral<"responds_to">, import("@sinclair/typebox").TLiteral<"references">, import("@sinclair/typebox").TLiteral<"inspired_by">, import("@sinclair/typebox").TLiteral<"follows">, import("@sinclair/typebox").TLiteral<"precedes">, import("@sinclair/typebox").TLiteral<"depends_on">, import("@sinclair/typebox").TLiteral<"composed_of">, import("@sinclair/typebox").TLiteral<"part_of">]>;
19
23
  //# sourceMappingURL=typebox.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"typebox.d.ts","sourceRoot":"","sources":["../../../src/types/typebox.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,EAAE,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;AAG9B,eAAO,MAAM,UAAU,qCAGrB,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;EAY3B,CAAC;AAEH,eAAO,MAAM,qBAAqB,qCAIhC,CAAC;AAEH,eAAO,MAAM,qBAAqB,qCAIhC,CAAC;AAEH,eAAO,MAAM,UAAU,yEAGrB,CAAC;AAEH,eAAO,MAAM,gBAAgB,8lBAYQ,CAAC;AAEtC,eAAO,MAAM,sBAAsB,yvCAoCjC,CAAC"}
1
+ {"version":3,"file":"typebox.d.ts","sourceRoot":"","sources":["../../../src/types/typebox.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,EAAE,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;AAG9B,eAAO,MAAM,UAAU,qCAGrB,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;EAY3B,CAAC;AAEH,eAAO,MAAM,qBAAqB,qCAIhC,CAAC;AAEH,eAAO,MAAM,qBAAqB,qCAIhC,CAAC;AAEH,eAAO,MAAM,UAAU,yEAGrB,CAAC;AAEH,eAAO,MAAM,gBAAgB,8lBAYQ,CAAC;AAGtC,eAAO,MAAM,eAAe,qCAE1B,CAAC;AAEH,eAAO,MAAM,aAAa,qCAExB,CAAC;AAEH,eAAO,MAAM,eAAe,oMAU3B,CAAC;AAEF,eAAO,MAAM,sBAAsB,qCAIjC,CAAC;AAEH,eAAO,MAAM,sBAAsB,yvCAoCjC,CAAC"}
@@ -52,6 +52,26 @@ export const MemoryTypeSchema = Type.Union([
52
52
  Type.Literal("relationship"),
53
53
  Type.Literal("strategy"),
54
54
  ], { description: "Type of memory" });
55
+ // Shared search parameter schemas (used by recall + search)
56
+ export const StartDateSchema = Type.String({
57
+ description: "Filter memories created on or after this date (ISO 8601, e.g. '2025-01-01')",
58
+ });
59
+ export const EndDateSchema = Type.String({
60
+ description: "Filter memories created on or before this date (ISO 8601, e.g. '2025-12-31')",
61
+ });
62
+ export const SortOrderSchema = Type.Union([
63
+ Type.Literal("relevance"),
64
+ Type.Literal("created_desc"),
65
+ Type.Literal("created_asc"),
66
+ ], {
67
+ description: 'Sort order: "relevance" (default), "created_desc", or "created_asc"',
68
+ default: "relevance",
69
+ });
70
+ export const MaxContentLengthSchema = Type.Number({
71
+ description: "Truncate memory content to this many characters. Full content available via penfield_fetch.",
72
+ minimum: 50,
73
+ maximum: 10000,
74
+ });
55
75
  export const RelationshipTypeSchema = Type.Union([
56
76
  // Knowledge Evolution
57
77
  Type.Literal("supersedes"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-penfield",
3
- "version": "1.1.2",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "description": "Native OpenClaw plugin for Penfield memory integration",
6
6
  "main": "dist/index.js",
@@ -15,7 +15,7 @@
15
15
  "memory",
16
16
  "knowledge-graph"
17
17
  ],
18
- "author": "Frank Fiegel",
18
+ "author": "Penfield",
19
19
  "license": "MIT",
20
20
  "repository": {
21
21
  "type": "git",