openclaw-penfield 1.0.6 → 1.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/README.md +86 -11
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -5
- package/dist/src/api-client.d.ts.map +1 -1
- package/dist/src/api-client.js +24 -8
- package/dist/src/auth-service.d.ts +1 -1
- package/dist/src/auth-service.js +1 -1
- package/dist/src/config.d.ts +6 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +2 -0
- package/dist/src/hooks.d.ts +31 -0
- package/dist/src/hooks.d.ts.map +1 -0
- package/dist/src/hooks.js +180 -0
- package/dist/src/tools/list-contexts.d.ts +3 -1
- package/dist/src/tools/list-contexts.d.ts.map +1 -1
- package/dist/src/tools/list-contexts.js +100 -12
- package/dist/src/tools/reflect.d.ts +4 -3
- package/dist/src/tools/reflect.d.ts.map +1 -1
- package/dist/src/tools/reflect.js +37 -16
- package/dist/src/types.d.ts +14 -0
- package/dist/src/types.d.ts.map +1 -1
- package/openclaw.plugin.json +3 -1
- package/package.json +6 -7
package/README.md
CHANGED
|
@@ -39,13 +39,12 @@ Penfield is in **free beta**. Sign up for access:
|
|
|
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
41
|
- **16 Memory Tools**
|
|
42
|
-
- **OAuth Device Code Flow**: Secure authentication following RFC 8628
|
|
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
|
|
45
45
|
- **Context Management**: Save and restore memory checkpoints
|
|
46
46
|
- **Artifact Storage**: Store and retrieve files in Penfield
|
|
47
47
|
- **Reflection & Analysis**: Analyze memory patterns and generate insights
|
|
48
|
-
- **Context Management**: Save and restore memory checkpoints
|
|
49
48
|
|
|
50
49
|
## Installation
|
|
51
50
|
|
|
@@ -65,11 +64,61 @@ openclaw plugins install -l .
|
|
|
65
64
|
|
|
66
65
|
## Configuration
|
|
67
66
|
|
|
68
|
-
The plugin is **auto-enabled when loaded**. No configuration required for
|
|
67
|
+
The plugin is **auto-enabled when loaded**. No configuration required for basic use.
|
|
68
|
+
|
|
69
|
+
### Plugin Config
|
|
70
|
+
|
|
71
|
+
In `openclaw.json` under `plugins.entries`:
|
|
72
|
+
|
|
73
|
+
| Option | Type | Default | Description |
|
|
74
|
+
|--------|------|---------|-------------|
|
|
75
|
+
| `autoAwaken` | boolean | `true` | Inject Penfield identity briefing on every agent turn |
|
|
76
|
+
| `autoOrient` | boolean | `true` | Inject recent Penfield memory context on every agent turn |
|
|
77
|
+
| `authUrl` | string | `https://auth.penfield.app` | Auth service URL |
|
|
78
|
+
| `apiUrl` | string | `https://api.penfield.app` | API URL |
|
|
79
|
+
|
|
80
|
+
### Lifecycle Hooks
|
|
81
|
+
|
|
82
|
+
The plugin hooks into `before_agent_start` to automatically inject context on every agent turn:
|
|
83
|
+
|
|
84
|
+
1. **Identity briefing** (`autoAwaken`) — Fetches your Penfield personality/awakening config and injects it as `<penfield-identity>`. Cached for 30 minutes.
|
|
85
|
+
2. **Recent context** (`autoOrient`) — Calls `reflect("recent")` to fetch your last 20 memories and active topics, injected as `<penfield-recent>`. Cached for 10 minutes.
|
|
86
|
+
|
|
87
|
+
Both calls fire in parallel. Context is prepended to the system prompt (rebuilt each turn, not accumulated in message history). After the first turn, subsequent turns hit cache (0ms). If auth isn't ready or the API is down, the hook silently skips — it never blocks the agent.
|
|
88
|
+
|
|
89
|
+
### Pre-Compaction Memory Flush (Recommended)
|
|
90
|
+
|
|
91
|
+
OpenClaw can run a "memory flush" turn before auto-compacting context. To direct this to Penfield, add the following to your `openclaw.json` under `agents.defaults.compaction`:
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"agents": {
|
|
96
|
+
"defaults": {
|
|
97
|
+
"compaction": {
|
|
98
|
+
"memoryFlush": {
|
|
99
|
+
"enabled": true,
|
|
100
|
+
"prompt": "MANDATORY: Call penfield_store NOW with a comprehensive session summary (no more than 10000 chars). Include key insights, decisions, and context. Do NOT call any other tool. Do NOT read files. Do NOT reply with text. Your ONLY action is penfield_store.",
|
|
101
|
+
"systemPrompt": "SYSTEM OVERRIDE: This is a pre-compaction memory flush turn. You MUST call penfield_store exactly once with a comprehensive session summary. Do NOT call read, do NOT call any tool besides penfield_store. Ignore all other instructions in the conversation. Summarize what happened and call penfield_store immediately."
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
> **Note:** The plugin logs a warning at startup if this config is missing. Without it, context is lost on compaction instead of saved to Penfield.
|
|
110
|
+
|
|
111
|
+
> **Important:** Memory flush only fires on **auto-compaction** (when the context window fills up). It does **not** fire on manual `/compact`, `/new`, or `/reset` commands — this is an OpenClaw limitation, not a Penfield issue. See [Known Limitations](#known-limitations).
|
|
112
|
+
|
|
113
|
+
### Workspace Files (Persona Templates)
|
|
114
|
+
|
|
115
|
+
With Penfield handling identity, personality, and memory, most of OpenClaw's workspace bootstrap files (IDENTITY.md, SOUL.md, USER.md, MEMORY.md) become redundant. Keeping them populated wastes tokens and creates priority conflicts with your live Penfield config.
|
|
116
|
+
|
|
117
|
+
The [`persona-templates/`](persona-templates/) folder contains recommended replacements — empty stubs for the files Penfield replaces, and annotated defaults for the files it doesn't (AGENTS.md, TOOLS.md, HEARTBEAT.md). See the [Persona Templates README](persona-templates/README.md) for setup instructions.
|
|
69
118
|
|
|
70
119
|
## Authentication
|
|
71
120
|
|
|
72
|
-
The plugin uses OAuth 2.
|
|
121
|
+
The plugin uses OAuth 2.1 Device Code Flow (RFC 8628) with automatic token refresh.
|
|
73
122
|
|
|
74
123
|
### CLI Login
|
|
75
124
|
|
|
@@ -231,11 +280,13 @@ Restore a previously saved checkpoint.
|
|
|
231
280
|
- `merge_mode` (optional): How to handle conflicts - "append", "replace", or "smart_merge" (default: "append")
|
|
232
281
|
|
|
233
282
|
#### `penfield_list_contexts`
|
|
234
|
-
List all saved checkpoints.
|
|
283
|
+
List all saved context checkpoints.
|
|
235
284
|
|
|
236
285
|
**Parameters:**
|
|
237
|
-
- `session_id` (optional): Filter by session ID
|
|
238
286
|
- `limit` (optional): Max results (default: 20, max: 100)
|
|
287
|
+
- `offset` (optional): Number of results to skip for pagination (default: 0)
|
|
288
|
+
- `name_pattern` (optional): Filter by name (case-insensitive substring match)
|
|
289
|
+
- `include_descriptions` (optional): Include full descriptions (default: false)
|
|
239
290
|
|
|
240
291
|
### Analysis
|
|
241
292
|
|
|
@@ -243,9 +294,10 @@ List all saved checkpoints.
|
|
|
243
294
|
Analyze memory patterns and generate insights.
|
|
244
295
|
|
|
245
296
|
**Parameters:**
|
|
246
|
-
- `time_window` (optional): Time
|
|
247
|
-
- `
|
|
248
|
-
- `
|
|
297
|
+
- `time_window` (optional): Time period - "recent" (default), "today"/"1d", "week"/"7d", "month"/"30d", or "90d"
|
|
298
|
+
- `start_date` (optional): Filter memories on or after this date (ISO 8601, e.g. "2025-01-01"). Overrides time_window.
|
|
299
|
+
- `end_date` (optional): Filter memories on or before this date (ISO 8601, e.g. "2025-01-31"). Overrides time_window.
|
|
300
|
+
- `include_documents` (optional): Include document chunks in analysis (default: false)
|
|
249
301
|
|
|
250
302
|
### Artifact Storage
|
|
251
303
|
|
|
@@ -348,6 +400,24 @@ await penfield_restore_context({
|
|
|
348
400
|
});
|
|
349
401
|
```
|
|
350
402
|
|
|
403
|
+
## Known Limitations
|
|
404
|
+
|
|
405
|
+
### Memory flush only fires on auto-compaction
|
|
406
|
+
|
|
407
|
+
OpenClaw's `memoryFlush` config only triggers when the context window fills up and auto-compaction kicks in. The following commands **bypass** memory flush entirely:
|
|
408
|
+
|
|
409
|
+
- `/compact` — compacts immediately, no flush
|
|
410
|
+
- `/new` — resets session, no flush
|
|
411
|
+
- `/reset` — resets session, no flush
|
|
412
|
+
|
|
413
|
+
This means context from shorter sessions (that never hit the auto-compaction threshold) won't be automatically saved to Penfield. To preserve important context before ending a session, tell your agent: *"Save this session to Penfield before we end."*
|
|
414
|
+
|
|
415
|
+
This is an OpenClaw limitation — the plugin has no way to intercept these commands.
|
|
416
|
+
|
|
417
|
+
### Auto-compaction threshold
|
|
418
|
+
|
|
419
|
+
Auto-compaction triggers when token usage reaches approximately `contextWindow - reserveTokensFloor - softThresholdTokens`. With defaults (200K context, 20K reserve, 4K soft threshold), flush fires around 176K tokens (~88% full). You can tune `softThresholdTokens` in `agents.defaults.compaction` to trigger earlier.
|
|
420
|
+
|
|
351
421
|
## Development
|
|
352
422
|
|
|
353
423
|
### Setup
|
|
@@ -390,6 +460,7 @@ src/
|
|
|
390
460
|
├── config.ts # Zod configuration schema with DEFAULT_AUTH_URL/DEFAULT_API_URL
|
|
391
461
|
├── types.ts # TypeScript type definitions (OpenClaw plugin API types)
|
|
392
462
|
├── types/typebox.ts # Centralized TypeBox exports
|
|
463
|
+
├── hooks.ts # Lifecycle hooks (auto-awaken, auto-orient, flush config check)
|
|
393
464
|
├── auth-service.ts # Background OAuth token refresh service
|
|
394
465
|
├── api-client.ts # HTTP client wrapper
|
|
395
466
|
├── runtime.ts # Runtime factory (receives authService from index.ts)
|
|
@@ -418,7 +489,7 @@ src/
|
|
|
418
489
|
|
|
419
490
|
## Service Lifecycle
|
|
420
491
|
|
|
421
|
-
The plugin uses two services registered with OpenClaw:
|
|
492
|
+
The plugin uses two services and one hook registered with OpenClaw:
|
|
422
493
|
|
|
423
494
|
1. **penfield-auth**: Background token refresh service
|
|
424
495
|
- Started when plugin loads
|
|
@@ -429,6 +500,10 @@ The plugin uses two services registered with OpenClaw:
|
|
|
429
500
|
- Manages runtime initialization
|
|
430
501
|
- Handles cleanup on shutdown
|
|
431
502
|
|
|
503
|
+
3. **before_agent_start hook**: Context injection
|
|
504
|
+
- Injects identity briefing + recent memories on every turn (cached)
|
|
505
|
+
- Checks memory flush config at startup and warns if not configured for Penfield
|
|
506
|
+
|
|
432
507
|
## API Endpoint Mapping
|
|
433
508
|
|
|
434
509
|
| Tool | Method | Endpoint |
|
|
@@ -439,7 +514,7 @@ The plugin uses two services registered with OpenClaw:
|
|
|
439
514
|
| explore | POST | /api/v2/relationships/traverse |
|
|
440
515
|
| fetch | GET | /api/v2/memories/{id} |
|
|
441
516
|
| list_artifacts | GET | /api/v2/artifacts/list |
|
|
442
|
-
| list_contexts | GET | /api/v2/checkpoint |
|
|
517
|
+
| list_contexts | GET | /api/v2/memories?memory_type=checkpoint |
|
|
443
518
|
| recall | POST | /api/v2/search/hybrid |
|
|
444
519
|
| reflect | POST | /api/v2/analysis/reflect |
|
|
445
520
|
| restore_context | POST | /api/v2/checkpoint/{id}/recall |
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAM9F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,QAAA,MAAM,cAAc;;;;;qBAMH,OAAO,GAAG,cAAc;;;;;;;;;;;;;;;;;;;;;;;;kBA2BzB,iBAAiB;CAyEhC,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DEFAULT_AUTH_URL, PenfieldConfigSchema } from "./src/config.js";
|
|
2
2
|
import { createPenfieldRuntime } from "./src/runtime.js";
|
|
3
3
|
import { registerPenfieldTools } from "./src/tools/index.js";
|
|
4
|
+
import { registerPenfieldHooks, resetHookCaches } from "./src/hooks.js";
|
|
4
5
|
import { registerLoginCommand } from "./src/cli.js";
|
|
5
6
|
import { createAuthService } from "./src/auth-service.js";
|
|
6
7
|
const penfieldPlugin = {
|
|
@@ -9,11 +10,7 @@ const penfieldPlugin = {
|
|
|
9
10
|
description: "Native Penfield memory integration with 16 tools for knowledge management",
|
|
10
11
|
configSchema: {
|
|
11
12
|
parse(value) {
|
|
12
|
-
|
|
13
|
-
if (value === undefined || value === null) {
|
|
14
|
-
return {};
|
|
15
|
-
}
|
|
16
|
-
return PenfieldConfigSchema.parse(value);
|
|
13
|
+
return PenfieldConfigSchema.parse(value ?? {});
|
|
17
14
|
},
|
|
18
15
|
uiHints: {
|
|
19
16
|
enabled: {
|
|
@@ -28,6 +25,14 @@ const penfieldPlugin = {
|
|
|
28
25
|
label: "API URL",
|
|
29
26
|
help: "Penfield API URL (default: https://api.penfield.app)",
|
|
30
27
|
},
|
|
28
|
+
autoAwaken: {
|
|
29
|
+
label: "Auto-Awaken",
|
|
30
|
+
help: "Inject Penfield identity briefing on every agent turn (default: true)",
|
|
31
|
+
},
|
|
32
|
+
autoOrient: {
|
|
33
|
+
label: "Auto-Orient",
|
|
34
|
+
help: "Inject recent Penfield memory context on every agent turn (default: true)",
|
|
35
|
+
},
|
|
31
36
|
},
|
|
32
37
|
},
|
|
33
38
|
register(api) {
|
|
@@ -52,6 +57,9 @@ const penfieldPlugin = {
|
|
|
52
57
|
config: cfg,
|
|
53
58
|
authService,
|
|
54
59
|
logger,
|
|
60
|
+
}).catch((err) => {
|
|
61
|
+
runtimePromise = null;
|
|
62
|
+
throw err;
|
|
55
63
|
});
|
|
56
64
|
}
|
|
57
65
|
runtime = await runtimePromise;
|
|
@@ -73,6 +81,8 @@ const penfieldPlugin = {
|
|
|
73
81
|
});
|
|
74
82
|
// Register all 16 tools
|
|
75
83
|
registerPenfieldTools(api, ensureRuntime);
|
|
84
|
+
// Register lifecycle hooks (auto-awaken + auto-orient, injected every turn)
|
|
85
|
+
registerPenfieldHooks({ api, config: cfg, ensureRuntime, logger });
|
|
76
86
|
// Register service for runtime lifecycle
|
|
77
87
|
api.registerService({
|
|
78
88
|
id: "penfield",
|
|
@@ -80,6 +90,7 @@ const penfieldPlugin = {
|
|
|
80
90
|
logger.info("[penfield] Service started");
|
|
81
91
|
},
|
|
82
92
|
async stop(_ctx) {
|
|
93
|
+
resetHookCaches();
|
|
83
94
|
if (runtime) {
|
|
84
95
|
await runtime.stop();
|
|
85
96
|
runtime = null;
|
|
@@ -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;
|
|
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"}
|
package/dist/src/api-client.js
CHANGED
|
@@ -15,14 +15,30 @@ export class PenfieldApiClient {
|
|
|
15
15
|
url += `?${params.toString()}`;
|
|
16
16
|
}
|
|
17
17
|
this.logger?.debug?.(`[penfield] ${method} ${endpoint}`);
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
const controller = new AbortController();
|
|
19
|
+
const timeoutId = setTimeout(() => controller.abort(), 30_000);
|
|
20
|
+
let response;
|
|
21
|
+
try {
|
|
22
|
+
response = await fetch(url, {
|
|
23
|
+
method,
|
|
24
|
+
headers: {
|
|
25
|
+
Authorization: `Bearer ${token}`,
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
},
|
|
28
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
29
|
+
signal: controller.signal,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
clearTimeout(timeoutId);
|
|
34
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
35
|
+
const msg = `Request timed out after 30s: ${method} ${endpoint}`;
|
|
36
|
+
this.logger?.error?.(`[penfield] ${msg}`);
|
|
37
|
+
throw new Error(msg);
|
|
38
|
+
}
|
|
39
|
+
throw err;
|
|
40
|
+
}
|
|
41
|
+
clearTimeout(timeoutId);
|
|
26
42
|
// Handle rate limiting
|
|
27
43
|
if (response.status === 429) {
|
|
28
44
|
const retryAfter = response.headers.get("Retry-After");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Background authentication service for Penfield plugin
|
|
3
3
|
*
|
|
4
|
-
* Handles OAuth 2.
|
|
4
|
+
* Handles OAuth 2.1 token refresh silently in the background
|
|
5
5
|
* without agent involvement. Registered as an OpenClaw service.
|
|
6
6
|
*
|
|
7
7
|
* Supports RFC 8628 Device Code Flow and RFC 9700 Token Rotation.
|
package/dist/src/auth-service.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Background authentication service for Penfield plugin
|
|
3
3
|
*
|
|
4
|
-
* Handles OAuth 2.
|
|
4
|
+
* Handles OAuth 2.1 token refresh silently in the background
|
|
5
5
|
* without agent involvement. Registered as an OpenClaw service.
|
|
6
6
|
*
|
|
7
7
|
* Supports RFC 8628 Device Code Flow and RFC 9700 Token Rotation.
|
package/dist/src/config.d.ts
CHANGED
|
@@ -5,12 +5,18 @@ export { DEFAULT_AUTH_URL, DEFAULT_API_URL };
|
|
|
5
5
|
export declare const PenfieldConfigSchema: z.ZodObject<{
|
|
6
6
|
authUrl: z.ZodOptional<z.ZodString>;
|
|
7
7
|
apiUrl: z.ZodOptional<z.ZodString>;
|
|
8
|
+
autoAwaken: z.ZodDefault<z.ZodBoolean>;
|
|
9
|
+
autoOrient: z.ZodDefault<z.ZodBoolean>;
|
|
8
10
|
}, "strict", z.ZodTypeAny, {
|
|
11
|
+
autoAwaken: boolean;
|
|
12
|
+
autoOrient: boolean;
|
|
9
13
|
authUrl?: string | undefined;
|
|
10
14
|
apiUrl?: string | undefined;
|
|
11
15
|
}, {
|
|
12
16
|
authUrl?: string | undefined;
|
|
13
17
|
apiUrl?: string | undefined;
|
|
18
|
+
autoAwaken?: boolean | undefined;
|
|
19
|
+
autoOrient?: boolean | undefined;
|
|
14
20
|
}>;
|
|
15
21
|
export type PenfieldConfig = z.infer<typeof PenfieldConfigSchema>;
|
|
16
22
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/src/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,QAAA,MAAM,gBAAgB,QAA+D,CAAC;AACtF,QAAA,MAAM,eAAe,QAA6D,CAAC;AAEnF,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC;AAO7C,eAAO,MAAM,oBAAoB
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,QAAA,MAAM,gBAAgB,QAA+D,CAAC;AACtF,QAAA,MAAM,eAAe,QAA6D,CAAC;AAEnF,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC;AAO7C,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;EAKtB,CAAC;AAEZ,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
|
package/dist/src/config.js
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Penfield lifecycle hooks for OpenClaw
|
|
3
|
+
*
|
|
4
|
+
* Hooks into OpenClaw's typed plugin hook system (api.on) to provide:
|
|
5
|
+
* - Auto-awaken: inject identity briefing via prependContext
|
|
6
|
+
* - Auto-orient: inject recent memory context via prependContext
|
|
7
|
+
*
|
|
8
|
+
* Uses before_agent_start hook (dispatched on every agent turn).
|
|
9
|
+
* prependContext is prepended to the system prompt (rebuilt each turn),
|
|
10
|
+
* so it does NOT accumulate in message history. This matches the pattern
|
|
11
|
+
* used by OpenClaw's own memory-lancedb plugin.
|
|
12
|
+
*
|
|
13
|
+
* Both the awaken briefing and recent context are cached to avoid
|
|
14
|
+
* redundant API calls on subsequent turns.
|
|
15
|
+
*
|
|
16
|
+
* Pre-compaction memory save is handled by OpenClaw's built-in memory flush,
|
|
17
|
+
* configured via agents.defaults.compaction.memoryFlush in openclaw.json.
|
|
18
|
+
*/
|
|
19
|
+
import type { PenfieldRuntime } from "./runtime.js";
|
|
20
|
+
import type { PenfieldConfig } from "./config.js";
|
|
21
|
+
import type { PluginLogger, OpenClawPluginApi } from "./types.js";
|
|
22
|
+
/** Clear hook caches — called on plugin stop to prevent stale state across restarts */
|
|
23
|
+
export declare function resetHookCaches(): void;
|
|
24
|
+
export interface RegisterHooksParams {
|
|
25
|
+
api: OpenClawPluginApi;
|
|
26
|
+
config: PenfieldConfig;
|
|
27
|
+
ensureRuntime: () => Promise<PenfieldRuntime>;
|
|
28
|
+
logger: PluginLogger;
|
|
29
|
+
}
|
|
30
|
+
export declare function registerPenfieldHooks(params: RegisterHooksParams): void;
|
|
31
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EAGlB,MAAM,YAAY,CAAC;AAmBpB,uFAAuF;AACvF,wBAAgB,eAAe,IAAI,IAAI,CAGtC;AAMD,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,iBAAiB,CAAC;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,aAAa,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;IAC9C,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAQvE"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Penfield lifecycle hooks for OpenClaw
|
|
3
|
+
*
|
|
4
|
+
* Hooks into OpenClaw's typed plugin hook system (api.on) to provide:
|
|
5
|
+
* - Auto-awaken: inject identity briefing via prependContext
|
|
6
|
+
* - Auto-orient: inject recent memory context via prependContext
|
|
7
|
+
*
|
|
8
|
+
* Uses before_agent_start hook (dispatched on every agent turn).
|
|
9
|
+
* prependContext is prepended to the system prompt (rebuilt each turn),
|
|
10
|
+
* so it does NOT accumulate in message history. This matches the pattern
|
|
11
|
+
* used by OpenClaw's own memory-lancedb plugin.
|
|
12
|
+
*
|
|
13
|
+
* Both the awaken briefing and recent context are cached to avoid
|
|
14
|
+
* redundant API calls on subsequent turns.
|
|
15
|
+
*
|
|
16
|
+
* Pre-compaction memory save is handled by OpenClaw's built-in memory flush,
|
|
17
|
+
* configured via agents.defaults.compaction.memoryFlush in openclaw.json.
|
|
18
|
+
*/
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Constants
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
const AWAKEN_CACHE_TTL_MS = 30 * 60 * 1000; // 30 minutes
|
|
23
|
+
const AWAKEN_MAX_CHARS = 30_000; // 30K char safety limit
|
|
24
|
+
const REFLECT_MAX_CHARS = 30_000; // 30K char safety limit
|
|
25
|
+
const REFLECT_CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes
|
|
26
|
+
const REFLECT_RECENT_LIMIT = 20;
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Module-level state
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
let awakenCache = null;
|
|
31
|
+
let reflectCache = null;
|
|
32
|
+
/** Clear hook caches — called on plugin stop to prevent stale state across restarts */
|
|
33
|
+
export function resetHookCaches() {
|
|
34
|
+
awakenCache = null;
|
|
35
|
+
reflectCache = null;
|
|
36
|
+
}
|
|
37
|
+
export function registerPenfieldHooks(params) {
|
|
38
|
+
const { api, config, ensureRuntime, logger } = params;
|
|
39
|
+
if (config.autoAwaken || config.autoOrient) {
|
|
40
|
+
registerBeforeAgentStartHook(api, config, ensureRuntime, logger);
|
|
41
|
+
}
|
|
42
|
+
checkMemoryFlushConfig(api, logger);
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// before_agent_start: awaken + orient (injected every turn, cached)
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
function registerBeforeAgentStartHook(api, config, ensureRuntime, logger) {
|
|
48
|
+
api.on("before_agent_start", async (_event, _ctx) => {
|
|
49
|
+
try {
|
|
50
|
+
let runtime;
|
|
51
|
+
try {
|
|
52
|
+
runtime = await ensureRuntime();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
logger.debug?.("[penfield] runtime not ready, skipping hook");
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
// Fire awaken and reflect in parallel (both cached after first call)
|
|
59
|
+
const [briefing, recentContext] = await Promise.all([
|
|
60
|
+
config.autoAwaken ? fetchAwakenBriefing(runtime, logger) : null,
|
|
61
|
+
config.autoOrient ? fetchRecentContext(runtime, logger) : null,
|
|
62
|
+
]);
|
|
63
|
+
const parts = [];
|
|
64
|
+
if (briefing) {
|
|
65
|
+
parts.push(`<penfield-identity>\n${briefing}\n</penfield-identity>`);
|
|
66
|
+
}
|
|
67
|
+
if (recentContext) {
|
|
68
|
+
parts.push(`<penfield-recent>\nRecent work context from Penfield:\n${recentContext}\n</penfield-recent>`);
|
|
69
|
+
}
|
|
70
|
+
if (parts.length === 0) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
return { prependContext: parts.join("\n\n") };
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
logger.warn(`[penfield] before_agent_start hook failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Awaken: fetch and cache identity briefing
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
async function fetchAwakenBriefing(runtime, logger) {
|
|
85
|
+
if (awakenCache && Date.now() - awakenCache.timestamp < AWAKEN_CACHE_TTL_MS) {
|
|
86
|
+
return awakenCache.briefing;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const response = await runtime.apiClient.get("/api/v2/personality/awakening");
|
|
90
|
+
if (!response || typeof response !== "object") {
|
|
91
|
+
logger.warn(`[penfield] auto-awaken: unexpected response type: ${typeof response}`);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const briefing = response.briefing || "";
|
|
95
|
+
if (!briefing) {
|
|
96
|
+
logger.warn(`[penfield] auto-awaken: API returned empty briefing (keys: ${Object.keys(response).join(", ")})`);
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
if (briefing.length > AWAKEN_MAX_CHARS) {
|
|
100
|
+
logger.warn(`[penfield] auto-awaken: briefing exceeds size limit (${briefing.length} > ${AWAKEN_MAX_CHARS}), skipping`);
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
awakenCache = { briefing, timestamp: Date.now() };
|
|
104
|
+
logger.info(`[penfield] auto-awaken: identity briefing loaded (${briefing.length} chars)`);
|
|
105
|
+
return briefing;
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
logger.warn(`[penfield] auto-awaken: failed to fetch briefing: ${err instanceof Error ? err.message : String(err)}`);
|
|
109
|
+
return awakenCache?.briefing ?? null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async function fetchRecentContext(runtime, logger) {
|
|
113
|
+
if (reflectCache && Date.now() - reflectCache.timestamp < REFLECT_CACHE_TTL_MS) {
|
|
114
|
+
return reflectCache.context;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const response = await runtime.apiClient.post("/api/v2/analysis/reflect", {
|
|
118
|
+
time_window: "recent",
|
|
119
|
+
max_memories: REFLECT_RECENT_LIMIT,
|
|
120
|
+
});
|
|
121
|
+
const memories = response.memories ?? [];
|
|
122
|
+
if (memories.length === 0) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const formatted = memories
|
|
126
|
+
.map((m) => {
|
|
127
|
+
const tag = m.memory_type ? `[${m.memory_type}]` : "";
|
|
128
|
+
return `- ${tag} ${m.content}`;
|
|
129
|
+
})
|
|
130
|
+
.join("\n");
|
|
131
|
+
const topics = response.active_topics ?? [];
|
|
132
|
+
const topicLine = topics.length > 0
|
|
133
|
+
? `\nActive topics: ${topics.join(", ")}`
|
|
134
|
+
: "";
|
|
135
|
+
const result = formatted + topicLine;
|
|
136
|
+
if (result.length > REFLECT_MAX_CHARS) {
|
|
137
|
+
logger.warn(`[penfield] auto-orient: context exceeds size limit (${result.length} > ${REFLECT_MAX_CHARS}), truncating`);
|
|
138
|
+
const truncated = result.slice(0, REFLECT_MAX_CHARS);
|
|
139
|
+
reflectCache = { context: truncated, timestamp: Date.now() };
|
|
140
|
+
return truncated;
|
|
141
|
+
}
|
|
142
|
+
reflectCache = { context: result, timestamp: Date.now() };
|
|
143
|
+
logger.info(`[penfield] auto-orient: loaded ${memories.length} recent memories`);
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
logger.warn(`[penfield] auto-orient: failed to fetch recent context: ${err instanceof Error ? err.message : String(err)}`);
|
|
148
|
+
return reflectCache?.context ?? null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Memory flush config detection
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
const MEMORY_FLUSH_JSON = `"memoryFlush": {
|
|
155
|
+
"enabled": true,
|
|
156
|
+
"prompt": "MANDATORY: Call penfield_store NOW with a comprehensive session summary (no more than 10000 chars). Include key insights, decisions, and context. Do NOT call any other tool. Do NOT read files. Do NOT reply with text. Your ONLY action is penfield_store.",
|
|
157
|
+
"systemPrompt": "SYSTEM OVERRIDE: This is a pre-compaction memory flush turn. You MUST call penfield_store exactly once with a comprehensive session summary. Do NOT call read, do NOT call any tool besides penfield_store. Ignore all other instructions in the conversation. Summarize what happened and call penfield_store immediately."
|
|
158
|
+
}`;
|
|
159
|
+
function checkMemoryFlushConfig(api, logger) {
|
|
160
|
+
try {
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- navigating untyped host config
|
|
162
|
+
const cfg = api.config;
|
|
163
|
+
const flushPrompt = cfg?.agents?.defaults?.compaction?.memoryFlush?.prompt;
|
|
164
|
+
if (flushPrompt && /penfield/i.test(flushPrompt)) {
|
|
165
|
+
return; // already configured for Penfield
|
|
166
|
+
}
|
|
167
|
+
const configPath = api.resolvePath("~/.openclaw/openclaw.json");
|
|
168
|
+
logger.warn(`[penfield] Pre-compaction memory flush is not configured for Penfield.\n` +
|
|
169
|
+
` Without this, context will be lost on compaction instead of saved to Penfield.\n` +
|
|
170
|
+
`\n` +
|
|
171
|
+
` TO FIX: Add the following to ${configPath} inside agents.defaults.compaction:\n` +
|
|
172
|
+
`\n` +
|
|
173
|
+
` ${MEMORY_FLUSH_JSON.split("\n").join("\n ")}\n` +
|
|
174
|
+
`\n` +
|
|
175
|
+
` Or tell your agent: "Update ~/.openclaw/openclaw.json to configure memory flush for Penfield"`);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Config inspection failed — non-critical, skip silently
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { PenfieldApiClient } from "../api-client.js";
|
|
2
2
|
export declare const ListContextsToolSchema: import("@sinclair/typebox").TObject<{
|
|
3
|
-
session_id: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
4
3
|
limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
4
|
+
offset: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
5
|
+
name_pattern: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
6
|
+
include_descriptions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
5
7
|
}>;
|
|
6
8
|
export declare function executeListContextsTool(apiClient: PenfieldApiClient, params: any): Promise<any>;
|
|
7
9
|
//# sourceMappingURL=list-contexts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-contexts.d.ts","sourceRoot":"","sources":["../../../src/tools/list-contexts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,eAAO,MAAM,sBAAsB
|
|
1
|
+
{"version":3,"file":"list-contexts.d.ts","sourceRoot":"","sources":["../../../src/tools/list-contexts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,eAAO,MAAM,sBAAsB;;;;;EA4BA,CAAC;AA+BpC,wBAAsB,uBAAuB,CAC3C,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,GAAG,GACV,OAAO,CAAC,GAAG,CAAC,CA8Gd"}
|
|
@@ -1,32 +1,120 @@
|
|
|
1
1
|
import { Type } from "../types/typebox.js";
|
|
2
2
|
export const ListContextsToolSchema = Type.Object({
|
|
3
|
-
session_id: Type.Optional(Type.String({
|
|
4
|
-
description: "Filter by session ID",
|
|
5
|
-
})),
|
|
6
3
|
limit: Type.Optional(Type.Number({
|
|
7
|
-
description: "Max results (default: 20, max: 100)",
|
|
4
|
+
description: "Max results to return (default: 20, max: 100)",
|
|
8
5
|
minimum: 1,
|
|
9
6
|
maximum: 100,
|
|
10
7
|
default: 20,
|
|
11
8
|
})),
|
|
9
|
+
offset: Type.Optional(Type.Number({
|
|
10
|
+
description: "Number of results to skip for pagination (default: 0)",
|
|
11
|
+
minimum: 0,
|
|
12
|
+
default: 0,
|
|
13
|
+
})),
|
|
14
|
+
name_pattern: Type.Optional(Type.String({
|
|
15
|
+
description: "Filter contexts by name (case-insensitive substring match)",
|
|
16
|
+
})),
|
|
17
|
+
include_descriptions: Type.Optional(Type.Boolean({
|
|
18
|
+
description: "Include full descriptions in output (default: false for compact listing)",
|
|
19
|
+
})),
|
|
12
20
|
}, { additionalProperties: false });
|
|
13
21
|
export async function executeListContextsTool(apiClient, params // eslint-disable-line @typescript-eslint/no-explicit-any -- validated by TypeBox schema
|
|
14
22
|
) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
23
|
+
const limit = params.limit || 20;
|
|
24
|
+
const offset = params.offset || 0;
|
|
25
|
+
const namePattern = params.name_pattern;
|
|
26
|
+
const lowerPattern = namePattern?.toLowerCase();
|
|
27
|
+
const includeDescriptions = params.include_descriptions;
|
|
28
|
+
// Without name_pattern: use API-side pagination directly (no cap)
|
|
29
|
+
// With name_pattern: fetch in bulk for client-side filtering
|
|
30
|
+
const queryParams = { memory_type: "checkpoint" };
|
|
31
|
+
if (!namePattern) {
|
|
32
|
+
// Translate offset/limit to page/per_page
|
|
33
|
+
const page = Math.floor(offset / limit) + 1;
|
|
34
|
+
queryParams.per_page = String(limit);
|
|
35
|
+
queryParams.page = String(page);
|
|
18
36
|
}
|
|
19
|
-
|
|
20
|
-
|
|
37
|
+
else {
|
|
38
|
+
// Fetch enough to cover filtering + pagination headroom
|
|
39
|
+
queryParams.per_page = String(Math.min(limit + offset + 50, 250));
|
|
21
40
|
}
|
|
22
|
-
const response = await apiClient.get("/api/v2/
|
|
41
|
+
const response = await apiClient.get("/api/v2/memories", queryParams);
|
|
42
|
+
const items = response.items ?? [];
|
|
43
|
+
const contexts = [];
|
|
44
|
+
for (const mem of items) {
|
|
45
|
+
// Extract checkpoint name from metadata (preferred) or content JSON (legacy)
|
|
46
|
+
let name = mem.metadata?.checkpoint_name;
|
|
47
|
+
let memoryCount = mem.metadata?.memory_count ?? 0;
|
|
48
|
+
let description;
|
|
49
|
+
if (!name) {
|
|
50
|
+
try {
|
|
51
|
+
const parsed = JSON.parse(mem.content);
|
|
52
|
+
name = parsed.checkpoint_name;
|
|
53
|
+
memoryCount = parsed.memory_count ?? memoryCount;
|
|
54
|
+
description = parsed.description;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Legacy format without JSON — use content as-is
|
|
58
|
+
name = undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (includeDescriptions) {
|
|
62
|
+
try {
|
|
63
|
+
const parsed = JSON.parse(mem.content);
|
|
64
|
+
description = parsed.description;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Content not JSON — no description available
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Apply name_pattern filter (case-insensitive substring)
|
|
71
|
+
if (lowerPattern) {
|
|
72
|
+
const haystack = (name ?? "").toLowerCase();
|
|
73
|
+
if (!haystack.includes(lowerPattern)) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const entry = {
|
|
78
|
+
id: mem.id,
|
|
79
|
+
name: name ?? "(unnamed)",
|
|
80
|
+
memory_count: memoryCount,
|
|
81
|
+
created: mem.created_at,
|
|
82
|
+
};
|
|
83
|
+
if (includeDescriptions && description) {
|
|
84
|
+
entry.description = description;
|
|
85
|
+
}
|
|
86
|
+
contexts.push(entry);
|
|
87
|
+
}
|
|
88
|
+
// Pagination: API-side when no filter, client-side when filtering
|
|
89
|
+
const apiTotal = response.pagination?.total ?? 0;
|
|
90
|
+
let paged;
|
|
91
|
+
let total;
|
|
92
|
+
if (!namePattern) {
|
|
93
|
+
// API already paginated — trim misaligned leading items
|
|
94
|
+
const skip = offset % limit;
|
|
95
|
+
paged = skip > 0 ? contexts.slice(skip) : contexts;
|
|
96
|
+
total = apiTotal;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Client-side pagination on filtered results
|
|
100
|
+
total = contexts.length;
|
|
101
|
+
paged = contexts.slice(offset, offset + limit);
|
|
102
|
+
}
|
|
103
|
+
const result = {
|
|
104
|
+
success: true,
|
|
105
|
+
contexts: paged,
|
|
106
|
+
total,
|
|
107
|
+
limit,
|
|
108
|
+
offset,
|
|
109
|
+
has_more: total > offset + paged.length,
|
|
110
|
+
};
|
|
23
111
|
return {
|
|
24
112
|
content: [
|
|
25
113
|
{
|
|
26
114
|
type: "text",
|
|
27
|
-
text: JSON.stringify(
|
|
115
|
+
text: JSON.stringify(result, null, 2),
|
|
28
116
|
},
|
|
29
117
|
],
|
|
30
|
-
details:
|
|
118
|
+
details: result,
|
|
31
119
|
};
|
|
32
120
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { PenfieldApiClient } from "../api-client.js";
|
|
2
2
|
export declare const ReflectToolSchema: import("@sinclair/typebox").TObject<{
|
|
3
|
-
time_window: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"1d">, import("@sinclair/typebox").TLiteral<"7d">, import("@sinclair/typebox").TLiteral<"30d">, import("@sinclair/typebox").TLiteral<"90d">]>>;
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
time_window: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"recent">, import("@sinclair/typebox").TLiteral<"today">, import("@sinclair/typebox").TLiteral<"week">, import("@sinclair/typebox").TLiteral<"month">, import("@sinclair/typebox").TLiteral<"1d">, import("@sinclair/typebox").TLiteral<"7d">, import("@sinclair/typebox").TLiteral<"30d">, import("@sinclair/typebox").TLiteral<"90d">]>>;
|
|
4
|
+
start_date: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
5
|
+
end_date: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
6
|
+
include_documents: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
6
7
|
}>;
|
|
7
8
|
export declare function executeReflectTool(apiClient: PenfieldApiClient, params: unknown): Promise<any>;
|
|
8
9
|
//# sourceMappingURL=reflect.d.ts.map
|
|
@@ -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
|
|
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,31 +1,52 @@
|
|
|
1
|
-
import { Type
|
|
1
|
+
import { Type } from "../types/typebox.js";
|
|
2
2
|
export const ReflectToolSchema = Type.Object({
|
|
3
3
|
time_window: Type.Optional(Type.Union([
|
|
4
|
+
Type.Literal("recent"),
|
|
5
|
+
Type.Literal("today"),
|
|
6
|
+
Type.Literal("week"),
|
|
7
|
+
Type.Literal("month"),
|
|
4
8
|
Type.Literal("1d"),
|
|
5
9
|
Type.Literal("7d"),
|
|
6
10
|
Type.Literal("30d"),
|
|
7
11
|
Type.Literal("90d"),
|
|
8
12
|
], {
|
|
9
|
-
description:
|
|
10
|
-
examples: ["
|
|
13
|
+
description: 'Time period to analyze: "recent" (default, no time cutoff), "today"/"1d", "week"/"7d", "month"/"30d", or "90d".',
|
|
14
|
+
examples: ["recent", "week", "30d"],
|
|
11
15
|
})),
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Type.Literal("importance"),
|
|
16
|
-
Type.Literal("topics"),
|
|
17
|
-
Type.Literal("patterns"),
|
|
18
|
-
]), {
|
|
19
|
-
description: "Areas to focus analysis on. Omit for full analysis.",
|
|
20
|
-
examples: [["memory_usage", "relationships"], ["topics"]],
|
|
16
|
+
start_date: Type.Optional(Type.String({
|
|
17
|
+
description: "Filter memories created on or after this date (ISO 8601, e.g. '2025-01-01'). Overrides time_window when set.",
|
|
18
|
+
examples: ["2025-01-01"],
|
|
21
19
|
})),
|
|
22
|
-
|
|
23
|
-
description: "Filter
|
|
24
|
-
examples: [
|
|
20
|
+
end_date: Type.Optional(Type.String({
|
|
21
|
+
description: "Filter memories created on or before this date (ISO 8601, e.g. '2025-01-31'). Overrides time_window when set.",
|
|
22
|
+
examples: ["2025-01-31"],
|
|
23
|
+
})),
|
|
24
|
+
include_documents: Type.Optional(Type.Boolean({
|
|
25
|
+
description: "Include document chunks in analysis. Default false (only user-created memories).",
|
|
25
26
|
})),
|
|
26
27
|
}, { additionalProperties: false });
|
|
28
|
+
/** Map shorthand aliases to API-native time_window values */
|
|
29
|
+
const TIME_WINDOW_ALIASES = {
|
|
30
|
+
"1d": "today",
|
|
31
|
+
"7d": "week",
|
|
32
|
+
"30d": "month",
|
|
33
|
+
};
|
|
27
34
|
export async function executeReflectTool(apiClient, params) {
|
|
28
|
-
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- params validated by schema
|
|
36
|
+
const p = { ...params };
|
|
37
|
+
if (p.time_window === "90d") {
|
|
38
|
+
// API has no 90d window — convert to explicit date range (matches MCP behavior)
|
|
39
|
+
if (!p.start_date) {
|
|
40
|
+
const d = new Date();
|
|
41
|
+
d.setDate(d.getDate() - 90);
|
|
42
|
+
p.start_date = d.toISOString().slice(0, 10);
|
|
43
|
+
}
|
|
44
|
+
p.time_window = "recent";
|
|
45
|
+
}
|
|
46
|
+
else if (p.time_window && p.time_window in TIME_WINDOW_ALIASES) {
|
|
47
|
+
p.time_window = TIME_WINDOW_ALIASES[p.time_window];
|
|
48
|
+
}
|
|
49
|
+
const response = await apiClient.post("/api/v2/analysis/reflect", p);
|
|
29
50
|
return {
|
|
30
51
|
content: [
|
|
31
52
|
{
|
package/dist/src/types.d.ts
CHANGED
|
@@ -40,6 +40,16 @@ export interface OpenClawPluginCliContext {
|
|
|
40
40
|
workspaceDir?: string;
|
|
41
41
|
logger: PluginLogger;
|
|
42
42
|
}
|
|
43
|
+
/** Event for before_agent_start hook */
|
|
44
|
+
export interface PluginHookBeforeAgentStartEvent {
|
|
45
|
+
prompt: string;
|
|
46
|
+
messages?: unknown[];
|
|
47
|
+
}
|
|
48
|
+
/** Result for before_agent_start hook — prependContext is injected before the agent prompt */
|
|
49
|
+
export interface PluginHookBeforeAgentStartResult {
|
|
50
|
+
systemPrompt?: string;
|
|
51
|
+
prependContext?: string;
|
|
52
|
+
}
|
|
43
53
|
export interface OpenClawPluginApi {
|
|
44
54
|
/** Plugin identifier */
|
|
45
55
|
id: string;
|
|
@@ -77,6 +87,10 @@ export interface OpenClawPluginApi {
|
|
|
77
87
|
}): void;
|
|
78
88
|
/** Register service lifecycle */
|
|
79
89
|
registerService(service: OpenClawPluginService): void;
|
|
90
|
+
/** Register a typed plugin lifecycle hook (always available, no config gate) */
|
|
91
|
+
on(hookName: string, handler: (...args: any[]) => any, opts?: {
|
|
92
|
+
priority?: number;
|
|
93
|
+
}): void;
|
|
80
94
|
/** Runtime context */
|
|
81
95
|
runtime?: {
|
|
82
96
|
prompter?: {
|
package/dist/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
package/openclaw.plugin.json
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
"additionalProperties": false,
|
|
7
7
|
"properties": {
|
|
8
8
|
"authUrl": { "type": "string" },
|
|
9
|
-
"apiUrl": { "type": "string" }
|
|
9
|
+
"apiUrl": { "type": "string" },
|
|
10
|
+
"autoAwaken": { "type": "boolean", "default": true },
|
|
11
|
+
"autoOrient": { "type": "boolean", "default": true }
|
|
10
12
|
}
|
|
11
13
|
}
|
|
12
14
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-penfield",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Native OpenClaw plugin for Penfield memory integration",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -39,16 +39,15 @@
|
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^25.0.9",
|
|
42
|
-
"
|
|
43
|
-
"@typescript-eslint/parser": "^7.0.0",
|
|
44
|
-
"eslint": "^8.56.0",
|
|
42
|
+
"eslint": "^9.39.2",
|
|
45
43
|
"prettier": "^3.2.5",
|
|
46
|
-
"typescript": "^5.3.3"
|
|
44
|
+
"typescript": "^5.3.3",
|
|
45
|
+
"typescript-eslint": "^8.54.0"
|
|
47
46
|
},
|
|
48
47
|
"scripts": {
|
|
49
48
|
"build": "tsc",
|
|
50
|
-
"lint": "eslint .
|
|
51
|
-
"lint:fix": "eslint . --
|
|
49
|
+
"lint": "eslint .",
|
|
50
|
+
"lint:fix": "eslint . --fix",
|
|
52
51
|
"format": "prettier --write \"**/*.{ts,json,md}\"",
|
|
53
52
|
"format:check": "prettier --check \"**/*.{ts,json,md}\"",
|
|
54
53
|
"typecheck": "tsc --noEmit"
|