clawvault 3.2.0 → 3.3.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 +54 -14
- package/bin/clawvault.js +0 -2
- package/bin/command-registration.test.js +13 -1
- package/bin/help-contract.test.js +14 -0
- package/bin/register-core-commands.js +88 -0
- package/bin/register-core-commands.test.js +80 -0
- package/bin/register-maintenance-commands.js +57 -6
- package/bin/register-query-commands.js +10 -28
- package/bin/test-helpers/cli-command-fixtures.js +1 -0
- package/dist/chunk-2PKBIKDH.js +130 -0
- package/dist/{chunk-2JQ3O2YL.js → chunk-5EFSWZO6.js} +3 -3
- package/dist/{chunk-77Q5CSPJ.js → chunk-7SWP5FKU.js} +33 -701
- package/dist/{chunk-URXDAUVH.js → chunk-AXSJIFOJ.js} +174 -1
- package/dist/{chunk-23YDQ3QU.js → chunk-BLQXXX7Q.js} +6 -6
- package/dist/chunk-CSHO3PJB.js +684 -0
- package/dist/{chunk-SLXOR3CC.js → chunk-DOIUYIXV.js} +2 -2
- package/dist/{chunk-NCKFNBHJ.js → chunk-DVOUSOR3.js} +79 -5
- package/dist/{chunk-CLJTREDS.js → chunk-ECGJYWNA.js} +193 -41
- package/dist/{chunk-BUEW6IIK.js → chunk-EL6UBSX5.js} +5 -5
- package/dist/{chunk-6FH3IULF.js → chunk-FZ5I2NF7.js} +1 -1
- package/dist/{chunk-ZN54U2OZ.js → chunk-GFCHWMGD.js} +3 -3
- package/dist/{chunk-GNJL4YGR.js → chunk-GJO3CFUN.js} +30 -6
- package/dist/chunk-H3JZIB5O.js +322 -0
- package/dist/chunk-HEHO7SMV.js +51 -0
- package/dist/{chunk-STCQGCEQ.js → chunk-HGDDW24U.js} +3 -3
- package/dist/chunk-J3YUXVID.js +907 -0
- package/dist/{chunk-Y6VJKXGL.js → chunk-KCYWJDDW.js} +1 -1
- package/dist/{chunk-W4SPAEE7.js → chunk-OFOCU2V4.js} +5 -4
- package/dist/chunk-PTWPPVC7.js +972 -0
- package/dist/{chunk-QSHD36LH.js → chunk-QFWERBDP.js} +2 -2
- package/dist/{chunk-QSRRMEYM.js → chunk-S7N7HI5E.js} +1 -1
- package/dist/{chunk-PBACDKKP.js → chunk-T7E764W3.js} +3 -3
- package/dist/chunk-TDWFBDAQ.js +1016 -0
- package/dist/{chunk-ESVS6K2B.js → chunk-TWMI3SNN.js} +6 -5
- package/dist/{chunk-2RAZ4ZFE.js → chunk-VBILES4B.js} +1 -1
- package/dist/{chunk-ESFLMDRB.js → chunk-VXAGOLDP.js} +3 -3
- package/dist/chunk-YCUVAOFC.js +158 -0
- package/dist/{chunk-SS4B7P7V.js → chunk-YIDV4VV2.js} +1 -1
- package/dist/chunk-ZKWPCBYT.js +600 -0
- package/dist/cli/index.js +24 -24
- package/dist/commands/archive.js +2 -2
- package/dist/commands/benchmark.d.ts +12 -0
- package/dist/commands/benchmark.js +12 -0
- package/dist/commands/context.js +6 -5
- package/dist/commands/doctor.d.ts +8 -3
- package/dist/commands/doctor.js +6 -20
- package/dist/commands/embed.js +5 -4
- package/dist/commands/entities.js +1 -1
- package/dist/commands/graph.js +2 -2
- package/dist/commands/inbox.d.ts +23 -0
- package/dist/commands/inbox.js +11 -0
- package/dist/commands/inject.d.ts +1 -1
- package/dist/commands/inject.js +3 -3
- package/dist/commands/link.js +6 -6
- package/dist/commands/maintain.d.ts +32 -0
- package/dist/commands/maintain.js +12 -0
- package/dist/commands/migrate-observations.js +2 -2
- package/dist/commands/observe.js +9 -8
- package/dist/commands/rebuild-embeddings.js +47 -16
- package/dist/commands/rebuild.js +7 -6
- package/dist/commands/reflect.js +5 -5
- package/dist/commands/replay.js +8 -7
- package/dist/commands/setup.js +3 -2
- package/dist/commands/sleep.d.ts +1 -1
- package/dist/commands/sleep.js +17 -15
- package/dist/commands/status.js +26 -24
- package/dist/commands/sync-bd.js +2 -2
- package/dist/commands/tailscale.js +2 -2
- package/dist/commands/wake.d.ts +1 -1
- package/dist/commands/wake.js +8 -7
- package/dist/index.d.ts +168 -16
- package/dist/index.js +271 -108
- package/dist/{inject-DYUrDqQO.d.ts → inject-DEb_jpLi.d.ts} +3 -1
- package/dist/lib/config.js +1 -1
- package/dist/{types-BbWJoC1c.d.ts → types-DslKvCaj.d.ts} +51 -1
- package/hooks/clawvault/HOOK.md +22 -5
- package/hooks/clawvault/handler.js +213 -78
- package/hooks/clawvault/handler.test.js +109 -43
- package/hooks/clawvault/integrity.js +112 -0
- package/hooks/clawvault/integrity.test.js +32 -0
- package/hooks/clawvault/openclaw.plugin.json +133 -15
- package/openclaw.plugin.json +126 -20
- package/package.json +2 -2
- package/bin/register-workgraph-commands.js +0 -1368
- package/dist/chunk-33VSQP4J.js +0 -37
- package/dist/chunk-4BQTQMJP.js +0 -93
- package/dist/chunk-EK6S23ZB.js +0 -469
- package/dist/chunk-GAOWA7GR.js +0 -501
- package/dist/chunk-GGA32J2R.js +0 -784
- package/dist/chunk-MM6QGW3P.js +0 -207
- package/dist/chunk-QVEERJSP.js +0 -152
- package/dist/chunk-U4O6C46S.js +0 -154
- package/dist/chunk-VSL7KY3M.js +0 -189
- package/dist/chunk-WMGIIABP.js +0 -15
- package/dist/commands/workgraph.d.ts +0 -124
- package/dist/commands/workgraph.js +0 -38
- package/dist/ledger-B7g7jhqG.d.ts +0 -44
- package/dist/registry-BR4326o0.d.ts +0 -30
- package/dist/store-CA-6sKCJ.d.ts +0 -34
- package/dist/thread-B9LhXNU0.d.ts +0 -41
- package/dist/workgraph/index.d.ts +0 -5
- package/dist/workgraph/index.js +0 -23
- package/dist/workgraph/ledger.d.ts +0 -2
- package/dist/workgraph/ledger.js +0 -25
- package/dist/workgraph/registry.d.ts +0 -2
- package/dist/workgraph/registry.js +0 -19
- package/dist/workgraph/store.d.ts +0 -2
- package/dist/workgraph/store.js +0 -25
- package/dist/workgraph/thread.d.ts +0 -2
- package/dist/workgraph/thread.js +0 -25
- package/dist/workgraph/types.d.ts +0 -54
- package/dist/workgraph/types.js +0 -7
package/hooks/clawvault/HOOK.md
CHANGED
|
@@ -87,20 +87,35 @@ openclaw config set plugins.entries.clawvault.config.vaultPath ~/my-vault
|
|
|
87
87
|
openclaw config get plugins.entries.clawvault
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
Available configuration options:
|
|
90
|
+
Available configuration options (all privileged actions are opt-in):
|
|
91
91
|
|
|
92
92
|
| Key | Type | Default | Description |
|
|
93
93
|
|-----|------|---------|-------------|
|
|
94
94
|
| `vaultPath` | string | (auto-detected) | Path to the ClawVault vault directory |
|
|
95
|
-
| `
|
|
95
|
+
| `agentVaults` | object | `{}` | Per-agent vault mapping |
|
|
96
|
+
| `allowClawvaultExec` | boolean | `false` | Required gate for all `child_process` calls |
|
|
97
|
+
| `clawvaultBinaryPath` | string | (PATH lookup) | Optional absolute path to `clawvault` binary |
|
|
98
|
+
| `clawvaultBinarySha256` | string | (unset) | Optional SHA-256 executable integrity check |
|
|
99
|
+
| `allowEnvAccess` | boolean | `false` | Allow env fallbacks (`OPENCLAW_*`, `CLAWVAULT_PATH`) |
|
|
100
|
+
| `enableStartupRecovery` | boolean | `false` | Enable `gateway:startup` recovery check |
|
|
101
|
+
| `enableSessionContextInjection` | boolean | `false` | Enable `session:start` recap/context injection |
|
|
102
|
+
| `enableAutoCheckpoint` | boolean | `false` | Enable checkpoint on `command:new` |
|
|
103
|
+
| `enableObserveOnNew` | boolean | `false` | Enable observer flush on `command:new` |
|
|
104
|
+
| `enableHeartbeatObservation` | boolean | `false` | Enable heartbeat-driven observation |
|
|
105
|
+
| `enableCompactionObservation` | boolean | `false` | Enable observer flush on compaction |
|
|
106
|
+
| `enableWeeklyReflection` | boolean | `false` | Enable weekly reflection cron |
|
|
107
|
+
| `enableFactExtraction` | boolean | `false` | Enable local fact extraction/entity graph updates |
|
|
108
|
+
| `autoCheckpoint` | boolean | `false` | Deprecated alias for `enableAutoCheckpoint` |
|
|
96
109
|
| `contextProfile` | string | `"auto"` | Default context profile (`default`, `planning`, `incident`, `handoff`, `auto`) |
|
|
97
110
|
| `maxContextResults` | integer | `4` | Maximum context results to inject on session start |
|
|
98
|
-
| `observeOnHeartbeat` | boolean | `
|
|
99
|
-
| `weeklyReflection` | boolean | `
|
|
111
|
+
| `observeOnHeartbeat` | boolean | `false` | Deprecated alias for `enableHeartbeatObservation` |
|
|
112
|
+
| `weeklyReflection` | boolean | `false` | Deprecated alias for `enableWeeklyReflection` |
|
|
113
|
+
|
|
114
|
+
Security details and threat model: see [SECURITY.md](../../SECURITY.md).
|
|
100
115
|
|
|
101
116
|
### Vault Path Resolution
|
|
102
117
|
|
|
103
|
-
|
|
118
|
+
When `allowEnvAccess=true`, the hook resolves the vault path in this order:
|
|
104
119
|
|
|
105
120
|
1. Plugin config (`plugins.entries.clawvault.config.vaultPath` set via `openclaw config`)
|
|
106
121
|
2. `OPENCLAW_PLUGIN_CLAWVAULT_VAULTPATH` environment variable
|
|
@@ -108,6 +123,8 @@ The hook resolves the vault path in this order:
|
|
|
108
123
|
4. Walking up from cwd to find `.clawvault.json`
|
|
109
124
|
5. Checking `memory/` subdirectory (OpenClaw convention)
|
|
110
125
|
|
|
126
|
+
When `allowEnvAccess=false` (default), steps 2 and 3 are skipped.
|
|
127
|
+
|
|
111
128
|
### Troubleshooting
|
|
112
129
|
|
|
113
130
|
If `openclaw hooks enable clawvault` fails with hook-not-found, run `openclaw hooks install clawvault` first and verify discovery with `openclaw hooks list --verbose`.
|
|
@@ -16,6 +16,11 @@ import { createHash, randomUUID } from 'crypto';
|
|
|
16
16
|
import * as fs from 'fs';
|
|
17
17
|
import * as os from 'os';
|
|
18
18
|
import * as path from 'path';
|
|
19
|
+
import {
|
|
20
|
+
resolveExecutablePath,
|
|
21
|
+
sanitizeExecArgs,
|
|
22
|
+
verifyExecutableIntegrity
|
|
23
|
+
} from './integrity.js';
|
|
19
24
|
|
|
20
25
|
const MAX_CONTEXT_RESULTS = 4;
|
|
21
26
|
const MAX_CONTEXT_PROMPT_LENGTH = 500;
|
|
@@ -36,6 +41,7 @@ const MAX_FACT_TEXT_LENGTH = 600;
|
|
|
36
41
|
const FACT_SENTENCE_SPLIT_RE = /[.!?]+\s+|\r?\n+/;
|
|
37
42
|
const EXCLUSIVE_FACT_RELATIONS = new Set(['lives_in', 'works_at', 'age']);
|
|
38
43
|
const ENTITY_TARGET_RELATIONS = new Set(['works_at', 'lives_in', 'partner_name', 'dog_name', 'parent_name']);
|
|
44
|
+
const CLAWVAULT_EXECUTABLE = 'clawvault';
|
|
39
45
|
|
|
40
46
|
// Sanitize string for safe display (prevent prompt injection via control chars)
|
|
41
47
|
function sanitizeForDisplay(str) {
|
|
@@ -107,15 +113,17 @@ function normalizeAbsoluteEnvPath(value) {
|
|
|
107
113
|
return resolved;
|
|
108
114
|
}
|
|
109
115
|
|
|
110
|
-
function getOpenClawAgentsDir() {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
function getOpenClawAgentsDir(pluginConfig) {
|
|
117
|
+
if (allowsEnvAccess(pluginConfig)) {
|
|
118
|
+
const stateDir = normalizeAbsoluteEnvPath(process.env.OPENCLAW_STATE_DIR);
|
|
119
|
+
if (stateDir) {
|
|
120
|
+
return path.join(stateDir, 'agents');
|
|
121
|
+
}
|
|
115
122
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
const openClawHome = normalizeAbsoluteEnvPath(process.env.OPENCLAW_HOME);
|
|
124
|
+
if (openClawHome) {
|
|
125
|
+
return path.join(openClawHome, 'agents');
|
|
126
|
+
}
|
|
119
127
|
}
|
|
120
128
|
|
|
121
129
|
return path.join(os.homedir(), '.openclaw', 'agents');
|
|
@@ -155,8 +163,8 @@ function getScaledObservationThresholdBytes(fileSizeBytes) {
|
|
|
155
163
|
return LARGE_SESSION_THRESHOLD_BYTES;
|
|
156
164
|
}
|
|
157
165
|
|
|
158
|
-
function parseSessionIndex(agentId) {
|
|
159
|
-
const sessionsDir = path.join(getOpenClawAgentsDir(), agentId, 'sessions');
|
|
166
|
+
function parseSessionIndex(agentId, pluginConfig) {
|
|
167
|
+
const sessionsDir = path.join(getOpenClawAgentsDir(pluginConfig), agentId, 'sessions');
|
|
160
168
|
const sessionsJsonPath = path.join(sessionsDir, 'sessions.json');
|
|
161
169
|
if (!fs.existsSync(sessionsJsonPath)) {
|
|
162
170
|
return { sessionsDir, index: {} };
|
|
@@ -173,9 +181,9 @@ function parseSessionIndex(agentId) {
|
|
|
173
181
|
}
|
|
174
182
|
}
|
|
175
183
|
|
|
176
|
-
function shouldObserveActiveSessions(vaultPath, agentId) {
|
|
184
|
+
function shouldObserveActiveSessions(vaultPath, agentId, pluginConfig) {
|
|
177
185
|
const cursors = loadObserveCursors(vaultPath);
|
|
178
|
-
const { sessionsDir, index } = parseSessionIndex(agentId);
|
|
186
|
+
const { sessionsDir, index } = parseSessionIndex(agentId, pluginConfig);
|
|
179
187
|
const entries = Object.entries(index);
|
|
180
188
|
if (entries.length === 0) {
|
|
181
189
|
return false;
|
|
@@ -472,6 +480,31 @@ function extractPluginConfig(event) {
|
|
|
472
480
|
return {};
|
|
473
481
|
}
|
|
474
482
|
|
|
483
|
+
function isOptInEnabled(pluginConfig, ...keys) {
|
|
484
|
+
for (const key of keys) {
|
|
485
|
+
if (pluginConfig?.[key] === true) return true;
|
|
486
|
+
}
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function allowsEnvAccess(pluginConfig) {
|
|
491
|
+
return isOptInEnabled(pluginConfig, 'allowEnvAccess');
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function getConfiguredExecutablePath(pluginConfig) {
|
|
495
|
+
const value = pluginConfig?.clawvaultBinaryPath;
|
|
496
|
+
if (typeof value !== 'string') return null;
|
|
497
|
+
const trimmed = value.trim();
|
|
498
|
+
return trimmed || null;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function getConfiguredExecutableSha256(pluginConfig) {
|
|
502
|
+
const value = pluginConfig?.clawvaultBinarySha256;
|
|
503
|
+
if (typeof value !== 'string') return null;
|
|
504
|
+
const trimmed = value.trim().toLowerCase();
|
|
505
|
+
return trimmed || null;
|
|
506
|
+
}
|
|
507
|
+
|
|
475
508
|
// Resolve vault path for a specific agent from agentVaults config
|
|
476
509
|
function resolveAgentVaultPath(pluginConfig, agentId) {
|
|
477
510
|
if (!agentId || typeof agentId !== 'string') return null;
|
|
@@ -489,11 +522,9 @@ function resolveAgentVaultPath(pluginConfig, agentId) {
|
|
|
489
522
|
|
|
490
523
|
// Find vault by walking up directories
|
|
491
524
|
// Supports per-agent vault paths via agentVaults config
|
|
492
|
-
function findVaultPath(event, options = {}) {
|
|
493
|
-
const pluginConfig = extractPluginConfig(event);
|
|
494
|
-
|
|
525
|
+
function findVaultPath(event, pluginConfig, options = {}) {
|
|
495
526
|
// Determine agent ID for per-agent vault resolution
|
|
496
|
-
const agentId = options.agentId || resolveAgentIdForEvent(event);
|
|
527
|
+
const agentId = options.agentId || resolveAgentIdForEvent(event, pluginConfig);
|
|
497
528
|
|
|
498
529
|
// Check agentVaults first (per-agent vault paths)
|
|
499
530
|
if (agentId) {
|
|
@@ -510,15 +541,17 @@ function findVaultPath(event, options = {}) {
|
|
|
510
541
|
if (validated) return validated;
|
|
511
542
|
}
|
|
512
543
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
544
|
+
if (allowsEnvAccess(pluginConfig)) {
|
|
545
|
+
// Check OPENCLAW_PLUGIN_CLAWVAULT_VAULTPATH env (injected by OpenClaw from plugin config)
|
|
546
|
+
if (process.env.OPENCLAW_PLUGIN_CLAWVAULT_VAULTPATH) {
|
|
547
|
+
const validated = validateVaultPath(process.env.OPENCLAW_PLUGIN_CLAWVAULT_VAULTPATH);
|
|
548
|
+
if (validated) return validated;
|
|
549
|
+
}
|
|
518
550
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
551
|
+
// Check CLAWVAULT_PATH env
|
|
552
|
+
if (process.env.CLAWVAULT_PATH) {
|
|
553
|
+
return validateVaultPath(process.env.CLAWVAULT_PATH);
|
|
554
|
+
}
|
|
522
555
|
}
|
|
523
556
|
|
|
524
557
|
// Walk up from cwd
|
|
@@ -541,16 +574,54 @@ function findVaultPath(event, options = {}) {
|
|
|
541
574
|
}
|
|
542
575
|
|
|
543
576
|
// Run clawvault command safely (no shell)
|
|
544
|
-
function runClawvault(args, options = {}) {
|
|
577
|
+
function runClawvault(args, pluginConfig, options = {}) {
|
|
578
|
+
if (!isOptInEnabled(pluginConfig, 'allowClawvaultExec')) {
|
|
579
|
+
return {
|
|
580
|
+
success: false,
|
|
581
|
+
skipped: true,
|
|
582
|
+
output: 'ClawVault CLI execution is disabled. Set allowClawvaultExec=true to enable.',
|
|
583
|
+
code: 0
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
545
587
|
const timeoutMs = Number.isFinite(options.timeoutMs) ? Math.max(1000, Number(options.timeoutMs)) : 15000;
|
|
588
|
+
const executablePath = resolveExecutablePath(CLAWVAULT_EXECUTABLE, {
|
|
589
|
+
explicitPath: getConfiguredExecutablePath(pluginConfig)
|
|
590
|
+
});
|
|
591
|
+
if (!executablePath) {
|
|
592
|
+
return {
|
|
593
|
+
success: false,
|
|
594
|
+
output: 'Unable to resolve clawvault executable path. Set clawvaultBinaryPath to an absolute executable path.',
|
|
595
|
+
code: 1
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
const expectedSha256 = getConfiguredExecutableSha256(pluginConfig);
|
|
600
|
+
const integrityResult = verifyExecutableIntegrity(executablePath, expectedSha256);
|
|
601
|
+
if (!integrityResult.ok) {
|
|
602
|
+
return {
|
|
603
|
+
success: false,
|
|
604
|
+
output: `Executable integrity verification failed for ${executablePath}.`,
|
|
605
|
+
code: 1
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
let sanitizedArgs;
|
|
546
610
|
try {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
611
|
+
sanitizedArgs = sanitizeExecArgs(args);
|
|
612
|
+
} catch (err) {
|
|
613
|
+
return {
|
|
614
|
+
success: false,
|
|
615
|
+
output: err?.message || 'Invalid command arguments',
|
|
616
|
+
code: 1
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
try {
|
|
621
|
+
const output = execFileSync(executablePath, sanitizedArgs, {
|
|
550
622
|
encoding: 'utf-8',
|
|
551
623
|
timeout: timeoutMs,
|
|
552
624
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
553
|
-
// Explicitly no shell
|
|
554
625
|
shell: false
|
|
555
626
|
});
|
|
556
627
|
return { success: true, output: output.trim(), code: 0 };
|
|
@@ -588,23 +659,29 @@ function parseRecoveryOutput(output) {
|
|
|
588
659
|
return { hadDeath, workingOn };
|
|
589
660
|
}
|
|
590
661
|
|
|
591
|
-
function resolveAgentIdForEvent(event) {
|
|
662
|
+
function resolveAgentIdForEvent(event, pluginConfig) {
|
|
592
663
|
const fromSessionKey = extractAgentIdFromSessionKey(extractSessionKey(event));
|
|
593
664
|
if (fromSessionKey) return fromSessionKey;
|
|
594
665
|
|
|
595
|
-
|
|
596
|
-
|
|
666
|
+
if (allowsEnvAccess(pluginConfig)) {
|
|
667
|
+
const fromEnv = sanitizeAgentId(process.env.OPENCLAW_AGENT_ID);
|
|
668
|
+
if (fromEnv) return fromEnv;
|
|
669
|
+
}
|
|
597
670
|
|
|
598
671
|
return 'main';
|
|
599
672
|
}
|
|
600
673
|
|
|
601
|
-
function runObserverCron(vaultPath, agentId, options = {}) {
|
|
674
|
+
function runObserverCron(vaultPath, agentId, pluginConfig, options = {}) {
|
|
602
675
|
const args = ['observe', '--cron', '--agent', agentId, '-v', vaultPath];
|
|
603
676
|
if (Number.isFinite(options.minNewBytes) && Number(options.minNewBytes) > 0) {
|
|
604
677
|
args.push('--min-new', String(Math.floor(Number(options.minNewBytes))));
|
|
605
678
|
}
|
|
606
679
|
|
|
607
|
-
const result = runClawvault(args, { timeoutMs: 120000 });
|
|
680
|
+
const result = runClawvault(args, pluginConfig, { timeoutMs: 120000 });
|
|
681
|
+
if (result.skipped) {
|
|
682
|
+
console.log('[clawvault] Observer cron skipped: allowClawvaultExec is disabled');
|
|
683
|
+
return false;
|
|
684
|
+
}
|
|
608
685
|
if (!result.success) {
|
|
609
686
|
console.warn(`[clawvault] Observer cron failed (${options.reason || 'unknown reason'})`);
|
|
610
687
|
return false;
|
|
@@ -1314,7 +1391,12 @@ function isSundayMidnightUtc(date) {
|
|
|
1314
1391
|
}
|
|
1315
1392
|
|
|
1316
1393
|
async function handleWeeklyReflect(event) {
|
|
1317
|
-
const
|
|
1394
|
+
const pluginConfig = extractPluginConfig(event);
|
|
1395
|
+
if (!isOptInEnabled(pluginConfig, 'enableWeeklyReflection', 'weeklyReflection')) {
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
const vaultPath = findVaultPath(event, pluginConfig);
|
|
1318
1400
|
if (!vaultPath) {
|
|
1319
1401
|
console.log('[clawvault] No vault found, skipping weekly reflection');
|
|
1320
1402
|
return;
|
|
@@ -1326,7 +1408,11 @@ async function handleWeeklyReflect(event) {
|
|
|
1326
1408
|
return;
|
|
1327
1409
|
}
|
|
1328
1410
|
|
|
1329
|
-
const result = runClawvault(['reflect', '-v', vaultPath], { timeoutMs: 120000 });
|
|
1411
|
+
const result = runClawvault(['reflect', '-v', vaultPath], pluginConfig, { timeoutMs: 120000 });
|
|
1412
|
+
if (result.skipped) {
|
|
1413
|
+
console.log('[clawvault] Weekly reflection skipped: allowClawvaultExec is disabled');
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1330
1416
|
if (!result.success) {
|
|
1331
1417
|
console.warn('[clawvault] Weekly reflection failed');
|
|
1332
1418
|
return;
|
|
@@ -1336,7 +1422,12 @@ async function handleWeeklyReflect(event) {
|
|
|
1336
1422
|
|
|
1337
1423
|
// Handle gateway startup - check for context death
|
|
1338
1424
|
async function handleStartup(event) {
|
|
1339
|
-
const
|
|
1425
|
+
const pluginConfig = extractPluginConfig(event);
|
|
1426
|
+
if (!isOptInEnabled(pluginConfig, 'enableStartupRecovery')) {
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
const vaultPath = findVaultPath(event, pluginConfig);
|
|
1340
1431
|
if (!vaultPath) {
|
|
1341
1432
|
console.log('[clawvault] No vault found, skipping recovery check');
|
|
1342
1433
|
return;
|
|
@@ -1345,7 +1436,11 @@ async function handleStartup(event) {
|
|
|
1345
1436
|
console.log(`[clawvault] Checking for context death`);
|
|
1346
1437
|
|
|
1347
1438
|
// Pass vault path as separate argument (not interpolated)
|
|
1348
|
-
const result = runClawvault(['recover', '--clear', '-v', vaultPath]);
|
|
1439
|
+
const result = runClawvault(['recover', '--clear', '-v', vaultPath], pluginConfig);
|
|
1440
|
+
if (result.skipped) {
|
|
1441
|
+
console.log('[clawvault] Recovery check skipped: allowClawvaultExec is disabled');
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1349
1444
|
|
|
1350
1445
|
if (!result.success) {
|
|
1351
1446
|
console.warn('[clawvault] Recovery check failed');
|
|
@@ -1375,7 +1470,15 @@ async function handleStartup(event) {
|
|
|
1375
1470
|
|
|
1376
1471
|
// Handle /new command - auto-checkpoint before reset
|
|
1377
1472
|
async function handleNew(event) {
|
|
1378
|
-
const
|
|
1473
|
+
const pluginConfig = extractPluginConfig(event);
|
|
1474
|
+
const autoCheckpointEnabled = isOptInEnabled(pluginConfig, 'enableAutoCheckpoint', 'autoCheckpoint');
|
|
1475
|
+
const observerOnNewEnabled = isOptInEnabled(pluginConfig, 'enableObserveOnNew');
|
|
1476
|
+
const factExtractionEnabled = isOptInEnabled(pluginConfig, 'enableFactExtraction');
|
|
1477
|
+
if (!autoCheckpointEnabled && !observerOnNewEnabled && !factExtractionEnabled) {
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
const vaultPath = findVaultPath(event, pluginConfig);
|
|
1379
1482
|
if (!vaultPath) {
|
|
1380
1483
|
console.log('[clawvault] No vault found, skipping auto-checkpoint');
|
|
1381
1484
|
return;
|
|
@@ -1389,33 +1492,44 @@ async function handleNew(event) {
|
|
|
1389
1492
|
? event.context.commandSource.replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 50)
|
|
1390
1493
|
: 'cli';
|
|
1391
1494
|
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
]);
|
|
1495
|
+
if (autoCheckpointEnabled) {
|
|
1496
|
+
console.log('[clawvault] Auto-checkpoint before /new');
|
|
1497
|
+
const result = runClawvault([
|
|
1498
|
+
'checkpoint',
|
|
1499
|
+
'--working-on', `Session reset via /new from ${source}`,
|
|
1500
|
+
'--focus', `Pre-reset checkpoint, session: ${sessionKey}`,
|
|
1501
|
+
'-v', vaultPath
|
|
1502
|
+
], pluginConfig);
|
|
1401
1503
|
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1504
|
+
if (result.skipped) {
|
|
1505
|
+
console.log('[clawvault] Auto-checkpoint skipped: allowClawvaultExec is disabled');
|
|
1506
|
+
} else if (result.success) {
|
|
1507
|
+
console.log('[clawvault] Auto-checkpoint created');
|
|
1508
|
+
} else {
|
|
1509
|
+
console.warn('[clawvault] Auto-checkpoint failed');
|
|
1510
|
+
}
|
|
1406
1511
|
}
|
|
1407
1512
|
|
|
1408
|
-
const agentId = resolveAgentIdForEvent(event);
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1513
|
+
const agentId = resolveAgentIdForEvent(event, pluginConfig);
|
|
1514
|
+
if (observerOnNewEnabled) {
|
|
1515
|
+
runObserverCron(vaultPath, agentId, pluginConfig, {
|
|
1516
|
+
minNewBytes: 1,
|
|
1517
|
+
reason: 'command:new flush'
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
if (factExtractionEnabled) {
|
|
1521
|
+
runFactExtractionForEvent(vaultPath, event, 'command:new');
|
|
1522
|
+
}
|
|
1414
1523
|
}
|
|
1415
1524
|
|
|
1416
1525
|
// Handle session start - inject dynamic context for first prompt
|
|
1417
1526
|
async function handleSessionStart(event) {
|
|
1418
|
-
const
|
|
1527
|
+
const pluginConfig = extractPluginConfig(event);
|
|
1528
|
+
if (!isOptInEnabled(pluginConfig, 'enableSessionContextInjection')) {
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
const vaultPath = findVaultPath(event, pluginConfig);
|
|
1419
1533
|
if (!vaultPath) {
|
|
1420
1534
|
console.log('[clawvault] No vault found, skipping context injection');
|
|
1421
1535
|
return;
|
|
@@ -1434,11 +1548,14 @@ async function handleSessionStart(event) {
|
|
|
1434
1548
|
recapArgs.push('--agent', agentId);
|
|
1435
1549
|
}
|
|
1436
1550
|
|
|
1437
|
-
const recapResult = runClawvault(recapArgs);
|
|
1438
|
-
if (
|
|
1439
|
-
console.
|
|
1440
|
-
}
|
|
1551
|
+
const recapResult = runClawvault(recapArgs, pluginConfig);
|
|
1552
|
+
if (recapResult.skipped) {
|
|
1553
|
+
console.log('[clawvault] Session recap skipped: allowClawvaultExec is disabled');
|
|
1554
|
+
}
|
|
1555
|
+
if (recapResult.success) {
|
|
1441
1556
|
recapEntries = parseSessionRecapJson(recapResult.output);
|
|
1557
|
+
} else if (!recapResult.skipped) {
|
|
1558
|
+
console.warn('[clawvault] Session recap lookup failed');
|
|
1442
1559
|
}
|
|
1443
1560
|
} else {
|
|
1444
1561
|
console.log('[clawvault] No session key found, skipping session recap');
|
|
@@ -1452,12 +1569,14 @@ async function handleSessionStart(event) {
|
|
|
1452
1569
|
'--format', 'json',
|
|
1453
1570
|
'--profile', 'auto',
|
|
1454
1571
|
'-v', vaultPath
|
|
1455
|
-
]);
|
|
1572
|
+
], pluginConfig);
|
|
1456
1573
|
|
|
1457
|
-
if (
|
|
1458
|
-
console.warn('[clawvault] Context lookup failed');
|
|
1459
|
-
} else {
|
|
1574
|
+
if (contextResult.success) {
|
|
1460
1575
|
memoryEntries = parseContextJson(contextResult.output);
|
|
1576
|
+
} else if (contextResult.skipped) {
|
|
1577
|
+
console.log('[clawvault] Context lookup skipped: allowClawvaultExec is disabled');
|
|
1578
|
+
} else {
|
|
1579
|
+
console.warn('[clawvault] Context lookup failed');
|
|
1461
1580
|
}
|
|
1462
1581
|
} else {
|
|
1463
1582
|
console.log('[clawvault] No initial prompt, skipping vault memory lookup');
|
|
@@ -1477,35 +1596,51 @@ async function handleSessionStart(event) {
|
|
|
1477
1596
|
|
|
1478
1597
|
// Handle heartbeat events - cheap stat-based trigger for active observation
|
|
1479
1598
|
async function handleHeartbeat(event) {
|
|
1480
|
-
const
|
|
1599
|
+
const pluginConfig = extractPluginConfig(event);
|
|
1600
|
+
if (!isOptInEnabled(pluginConfig, 'enableHeartbeatObservation', 'observeOnHeartbeat')) {
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
const vaultPath = findVaultPath(event, pluginConfig);
|
|
1481
1605
|
if (!vaultPath) {
|
|
1482
1606
|
console.log('[clawvault] No vault found, skipping heartbeat observation check');
|
|
1483
1607
|
return;
|
|
1484
1608
|
}
|
|
1485
1609
|
|
|
1486
|
-
const agentId = resolveAgentIdForEvent(event);
|
|
1487
|
-
if (!shouldObserveActiveSessions(vaultPath, agentId)) {
|
|
1610
|
+
const agentId = resolveAgentIdForEvent(event, pluginConfig);
|
|
1611
|
+
if (!shouldObserveActiveSessions(vaultPath, agentId, pluginConfig)) {
|
|
1488
1612
|
console.log('[clawvault] Heartbeat: no sessions crossed active-observe threshold');
|
|
1489
1613
|
return;
|
|
1490
1614
|
}
|
|
1491
1615
|
|
|
1492
|
-
runObserverCron(vaultPath, agentId, { reason: 'heartbeat threshold crossed' });
|
|
1616
|
+
runObserverCron(vaultPath, agentId, pluginConfig, { reason: 'heartbeat threshold crossed' });
|
|
1493
1617
|
}
|
|
1494
1618
|
|
|
1495
1619
|
// Handle context compaction - force flush any pending session deltas
|
|
1496
1620
|
async function handleContextCompaction(event) {
|
|
1497
|
-
const
|
|
1621
|
+
const pluginConfig = extractPluginConfig(event);
|
|
1622
|
+
const compactionObserveEnabled = isOptInEnabled(pluginConfig, 'enableCompactionObservation');
|
|
1623
|
+
const factExtractionEnabled = isOptInEnabled(pluginConfig, 'enableFactExtraction');
|
|
1624
|
+
if (!compactionObserveEnabled && !factExtractionEnabled) {
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
const vaultPath = findVaultPath(event, pluginConfig);
|
|
1498
1629
|
if (!vaultPath) {
|
|
1499
1630
|
console.log('[clawvault] No vault found, skipping compaction observation');
|
|
1500
1631
|
return;
|
|
1501
1632
|
}
|
|
1502
1633
|
|
|
1503
|
-
const agentId = resolveAgentIdForEvent(event);
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1634
|
+
const agentId = resolveAgentIdForEvent(event, pluginConfig);
|
|
1635
|
+
if (compactionObserveEnabled) {
|
|
1636
|
+
runObserverCron(vaultPath, agentId, pluginConfig, {
|
|
1637
|
+
minNewBytes: 1,
|
|
1638
|
+
reason: 'context compaction'
|
|
1639
|
+
});
|
|
1640
|
+
}
|
|
1641
|
+
if (factExtractionEnabled) {
|
|
1642
|
+
runFactExtractionForEvent(vaultPath, event, 'compaction:memoryFlush');
|
|
1643
|
+
}
|
|
1509
1644
|
}
|
|
1510
1645
|
|
|
1511
1646
|
// Main handler - route events
|