@karmaniverous/jeeves 0.5.0 → 0.5.1
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/content/skill.md +6 -0
- package/dist/cli/jeeves/index.js +9 -3
- package/dist/cli/plugin/index.js +9 -3
- package/dist/index.d.ts +23 -1
- package/dist/index.js +157 -90
- package/package.json +1 -1
package/content/skill.md
CHANGED
|
@@ -97,3 +97,9 @@ MEMORY.md has a character budget (default 20,000). Core tracks:
|
|
|
97
97
|
- Evergreen sections (no dates) are never flagged
|
|
98
98
|
|
|
99
99
|
Review is human/agent-mediated — core does not auto-delete.
|
|
100
|
+
|
|
101
|
+
### HEARTBEAT Integration
|
|
102
|
+
|
|
103
|
+
Memory hygiene is checked on every `ComponentWriter` cycle alongside component health. When budget or staleness thresholds are breached, a `## MEMORY.md` alert appears in HEARTBEAT.md under `# Jeeves Platform Status`. The alert includes character count, budget usage percentage, and any stale section names. When memory is healthy, the heading is absent — no alert content, no LLM cost on heartbeat polls.
|
|
104
|
+
|
|
105
|
+
The `## MEMORY.md` heading follows the same declined/active lifecycle as component headings (`## jeeves-{name}`). Users can decline memory alerts by changing the heading to `## MEMORY.md: declined`.
|
package/dist/cli/jeeves/index.js
CHANGED
|
@@ -268,14 +268,14 @@ const PLATFORM_COMPONENTS = [
|
|
|
268
268
|
* Core library version, inlined at build time.
|
|
269
269
|
*
|
|
270
270
|
* @remarks
|
|
271
|
-
* The `0.
|
|
271
|
+
* The `0.5.0` placeholder is replaced by
|
|
272
272
|
* `@rollup/plugin-replace` during the build with the actual version
|
|
273
273
|
* from `package.json`. This ensures the correct version survives
|
|
274
274
|
* when consumers bundle core into their own dist (where runtime
|
|
275
275
|
* `import.meta.url`-based resolution would find the wrong package.json).
|
|
276
276
|
*/
|
|
277
277
|
/** The core library version from package.json (inlined at build time). */
|
|
278
|
-
const CORE_VERSION = '0.
|
|
278
|
+
const CORE_VERSION = '0.5.0';
|
|
279
279
|
|
|
280
280
|
/**
|
|
281
281
|
* Runtime Node.js version floor check.
|
|
@@ -1007,7 +1007,7 @@ function parseHeartbeat(fileContent) {
|
|
|
1007
1007
|
const userContent = fileContent.slice(0, headingIndex).trim();
|
|
1008
1008
|
const sectionContent = fileContent.slice(headingIndex + HEARTBEAT_HEADING.length);
|
|
1009
1009
|
const entries = [];
|
|
1010
|
-
const h2Re = /^## (jeeves-\S
|
|
1010
|
+
const h2Re = /^## (jeeves-\S+?|MEMORY\.md)(?:: declined)?$/gm;
|
|
1011
1011
|
let match;
|
|
1012
1012
|
const h2Positions = [];
|
|
1013
1013
|
while ((match = h2Re.exec(sectionContent)) !== null) {
|
|
@@ -2109,6 +2109,12 @@ MEMORY.md has a character budget (default 20,000). Core tracks:
|
|
|
2109
2109
|
- Evergreen sections (no dates) are never flagged
|
|
2110
2110
|
|
|
2111
2111
|
Review is human/agent-mediated — core does not auto-delete.
|
|
2112
|
+
|
|
2113
|
+
### HEARTBEAT Integration
|
|
2114
|
+
|
|
2115
|
+
Memory hygiene is checked on every \`ComponentWriter\` cycle alongside component health. When budget or staleness thresholds are breached, a \`## MEMORY.md\` alert appears in HEARTBEAT.md under \`# Jeeves Platform Status\`. The alert includes character count, budget usage percentage, and any stale section names. When memory is healthy, the heading is absent — no alert content, no LLM cost on heartbeat polls.
|
|
2116
|
+
|
|
2117
|
+
The \`## MEMORY.md\` heading follows the same declined/active lifecycle as component headings (\`## jeeves-{name}\`). Users can decline memory alerts by changing the heading to \`## MEMORY.md: declined\`.
|
|
2112
2118
|
`;
|
|
2113
2119
|
|
|
2114
2120
|
/**
|
package/dist/cli/plugin/index.js
CHANGED
|
@@ -126,14 +126,14 @@ const COMPONENT_VERSIONS_FILE = 'component-versions.json';
|
|
|
126
126
|
* Core library version, inlined at build time.
|
|
127
127
|
*
|
|
128
128
|
* @remarks
|
|
129
|
-
* The `0.
|
|
129
|
+
* The `0.5.0` placeholder is replaced by
|
|
130
130
|
* `@rollup/plugin-replace` during the build with the actual version
|
|
131
131
|
* from `package.json`. This ensures the correct version survives
|
|
132
132
|
* when consumers bundle core into their own dist (where runtime
|
|
133
133
|
* `import.meta.url`-based resolution would find the wrong package.json).
|
|
134
134
|
*/
|
|
135
135
|
/** The core library version from package.json (inlined at build time). */
|
|
136
|
-
const CORE_VERSION = '0.
|
|
136
|
+
const CORE_VERSION = '0.5.0';
|
|
137
137
|
|
|
138
138
|
/**
|
|
139
139
|
* Shared file I/O helpers for managed section operations.
|
|
@@ -402,7 +402,7 @@ function parseHeartbeat(fileContent) {
|
|
|
402
402
|
const userContent = fileContent.slice(0, headingIndex).trim();
|
|
403
403
|
const sectionContent = fileContent.slice(headingIndex + HEARTBEAT_HEADING.length);
|
|
404
404
|
const entries = [];
|
|
405
|
-
const h2Re = /^## (jeeves-\S
|
|
405
|
+
const h2Re = /^## (jeeves-\S+?|MEMORY\.md)(?:: declined)?$/gm;
|
|
406
406
|
let match;
|
|
407
407
|
const h2Positions = [];
|
|
408
408
|
while ((match = h2Re.exec(sectionContent)) !== null) {
|
|
@@ -813,6 +813,12 @@ MEMORY.md has a character budget (default 20,000). Core tracks:
|
|
|
813
813
|
- Evergreen sections (no dates) are never flagged
|
|
814
814
|
|
|
815
815
|
Review is human/agent-mediated — core does not auto-delete.
|
|
816
|
+
|
|
817
|
+
### HEARTBEAT Integration
|
|
818
|
+
|
|
819
|
+
Memory hygiene is checked on every \`ComponentWriter\` cycle alongside component health. When budget or staleness thresholds are breached, a \`## MEMORY.md\` alert appears in HEARTBEAT.md under \`# Jeeves Platform Status\`. The alert includes character count, budget usage percentage, and any stale section names. When memory is healthy, the heading is absent — no alert content, no LLM cost on heartbeat polls.
|
|
820
|
+
|
|
821
|
+
The \`## MEMORY.md\` heading follows the same declined/active lifecycle as component headings (\`## jeeves-{name}\`). Users can decline memory alerts by changing the heading to \`## MEMORY.md: declined\`.
|
|
816
822
|
`;
|
|
817
823
|
|
|
818
824
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1355,6 +1355,28 @@ declare function extractMostRecentDate(text: string): Date | undefined;
|
|
|
1355
1355
|
*/
|
|
1356
1356
|
declare function analyzeMemory(options: MemoryHygieneOptions): MemoryHygieneResult;
|
|
1357
1357
|
|
|
1358
|
+
/**
|
|
1359
|
+
* HEARTBEAT integration for memory hygiene.
|
|
1360
|
+
*
|
|
1361
|
+
* @remarks
|
|
1362
|
+
* Calls `analyzeMemory()` and converts the result into a `HeartbeatEntry`
|
|
1363
|
+
* suitable for inclusion in the HEARTBEAT.md platform status section.
|
|
1364
|
+
* Returns `undefined` when MEMORY.md is healthy (no alert needed).
|
|
1365
|
+
*
|
|
1366
|
+
* Uses the `## MEMORY.md` heading (Decision 50) to distinguish memory
|
|
1367
|
+
* alerts from component alerts (`## jeeves-{name}`).
|
|
1368
|
+
*/
|
|
1369
|
+
|
|
1370
|
+
/** The HEARTBEAT heading name for memory alerts. */
|
|
1371
|
+
declare const MEMORY_HEARTBEAT_NAME = "MEMORY.md";
|
|
1372
|
+
/**
|
|
1373
|
+
* Check memory health and return a HEARTBEAT entry if unhealthy.
|
|
1374
|
+
*
|
|
1375
|
+
* @param options - Memory hygiene options (workspacePath, budget, etc.).
|
|
1376
|
+
* @returns A `HeartbeatEntry` when memory needs attention, `undefined` when healthy.
|
|
1377
|
+
*/
|
|
1378
|
+
declare function checkMemoryHealth(options: MemoryHygieneOptions): HeartbeatEntry | undefined;
|
|
1379
|
+
|
|
1358
1380
|
/**
|
|
1359
1381
|
* Internal function to maintain SOUL.md, AGENTS.md, and TOOLS.md Platform section.
|
|
1360
1382
|
*
|
|
@@ -1816,5 +1838,5 @@ interface ServiceManager {
|
|
|
1816
1838
|
*/
|
|
1817
1839
|
declare function createServiceManager(descriptor: JeevesComponentDescriptor): ServiceManager;
|
|
1818
1840
|
|
|
1819
|
-
export { AGENTS_MARKERS, CLEANUP_FLAG, COMPONENT_CONFIG_PREFIX, COMPONENT_VERSIONS_FILE, CONFIG_FILE, CORE_CONFIG_DIR, CORE_VERSION, ComponentWriter, DEFAULT_BIND_ADDRESS, DEFAULT_CORE_VERSION, DEFAULT_PORTS, HEARTBEAT_HEADING, JEEVES_SKILL_DIR, META_PORT, PLATFORM_COMPONENTS, REGISTRY_CACHE_FILE, RUNNER_PORT, SECTION_IDS, SECTION_ORDER, SERVER_PORT, SKILLS_DIR, SOUL_MARKERS, STALENESS_THRESHOLD_MS, STALE_LOCK_MS, TEMPLATES_DIR, TOOLS_MARKERS, VERSION_STAMP_PATTERN, WATCHER_PORT, WORKSPACE_CONFIG_DEFAULTS, WORKSPACE_CONFIG_FILE, WORKSPACE_FILES, analyzeMemory, appendJsonl, atomicWrite, buildEffectiveConfig, buildHeartbeatSection, checkNodeVersion, checkRegistryVersion, connectionFail, coreConfigSchema, createAsyncContentCache, createComponentWriter, createConfigApplyHandler, createConfigQueryHandler, createGoogleAuth, createPluginCli, createPluginToolset, createServiceCli, createServiceManager, createStatusHandler, ensureDir, extractMostRecentDate, fail, fetchJson, fetchWithTimeout, formatBeginMarker, formatEndMarker, generateJsonSchema, generateWorkspaceJsonSchema, getArg, getBindAddress, getChannelWorkspace, getComponentConfigDir, getConfigRoot, getCoreConfigDir, getCoreConfigFile, getEffectiveServiceName, getPackageVersion, getServiceState, getServiceUrl, getWorkspacePath, init, isPrime, jaccard, jeevesComponentDescriptorSchema, loadEnvFile, loadWorkspaceConfig, needsCleanup, nowIso, ok, orchestrateHeartbeat, parseArgs, parseHeartbeat, parseManaged, patchConfig, postJson, readComponentVersions, readJson, readJsonl, refreshPlatformContent, removeComponentVersion, removeManagedSection, resetInit, resolveConfigPath, resolveConfigValue, resolveOpenClawHome, resolveOptionalPluginSetting, resolvePluginSetting, resolveWorkspacePath, run, runScript, runWithRetry, saveCache, seedContent, seedSkill, shingles, shouldWrite, sleepAsync, sleepMs, updateManagedSection, uuid, withFileLock, workspaceConfigSchema, writeComponentVersion, writeHeartbeatSection, writeJsonAtomic, writeJsonl };
|
|
1841
|
+
export { AGENTS_MARKERS, CLEANUP_FLAG, COMPONENT_CONFIG_PREFIX, COMPONENT_VERSIONS_FILE, CONFIG_FILE, CORE_CONFIG_DIR, CORE_VERSION, ComponentWriter, DEFAULT_BIND_ADDRESS, DEFAULT_CORE_VERSION, DEFAULT_PORTS, HEARTBEAT_HEADING, JEEVES_SKILL_DIR, MEMORY_HEARTBEAT_NAME, META_PORT, PLATFORM_COMPONENTS, REGISTRY_CACHE_FILE, RUNNER_PORT, SECTION_IDS, SECTION_ORDER, SERVER_PORT, SKILLS_DIR, SOUL_MARKERS, STALENESS_THRESHOLD_MS, STALE_LOCK_MS, TEMPLATES_DIR, TOOLS_MARKERS, VERSION_STAMP_PATTERN, WATCHER_PORT, WORKSPACE_CONFIG_DEFAULTS, WORKSPACE_CONFIG_FILE, WORKSPACE_FILES, analyzeMemory, appendJsonl, atomicWrite, buildEffectiveConfig, buildHeartbeatSection, checkMemoryHealth, checkNodeVersion, checkRegistryVersion, connectionFail, coreConfigSchema, createAsyncContentCache, createComponentWriter, createConfigApplyHandler, createConfigQueryHandler, createGoogleAuth, createPluginCli, createPluginToolset, createServiceCli, createServiceManager, createStatusHandler, ensureDir, extractMostRecentDate, fail, fetchJson, fetchWithTimeout, formatBeginMarker, formatEndMarker, generateJsonSchema, generateWorkspaceJsonSchema, getArg, getBindAddress, getChannelWorkspace, getComponentConfigDir, getConfigRoot, getCoreConfigDir, getCoreConfigFile, getEffectiveServiceName, getPackageVersion, getServiceState, getServiceUrl, getWorkspacePath, init, isPrime, jaccard, jeevesComponentDescriptorSchema, loadEnvFile, loadWorkspaceConfig, needsCleanup, nowIso, ok, orchestrateHeartbeat, parseArgs, parseHeartbeat, parseManaged, patchConfig, postJson, readComponentVersions, readJson, readJsonl, refreshPlatformContent, removeComponentVersion, removeManagedSection, resetInit, resolveConfigPath, resolveConfigValue, resolveOpenClawHome, resolveOptionalPluginSetting, resolvePluginSetting, resolveWorkspacePath, run, runScript, runWithRetry, saveCache, seedContent, seedSkill, shingles, shouldWrite, sleepAsync, sleepMs, updateManagedSection, uuid, withFileLock, workspaceConfigSchema, writeComponentVersion, writeHeartbeatSection, writeJsonAtomic, writeJsonl };
|
|
1820
1842
|
export type { AccountConfig, AsyncContentCacheOptions, ComponentDependencies, ComponentState, ComponentVersionEntry, ComponentVersionsState, ComponentWriterOptions, ConfigApplyHandler, ConfigApplyRequest, ConfigApplyResult, ConfigProvenance, ConfigQueryHandler, ConfigQueryResponse, CoreConfig, CreatePluginCliOptions, CreateStatusHandlerOptions, GoogleAuthOptions, HeartbeatEntry, InitOptions, JeevesComponentDescriptor, ManagedMarkers, ManagedSection, MemoryHygieneOptions, MemoryHygieneResult, OrchestrateHeartbeatOptions, ParseManagedResult, ParsedHeartbeat, PlatformComponent, PluginApi, RefreshPlatformContentOptions, RemoveManagedSectionOptions, ResolvedValue, RetryOptions, RunOptions, SectionId, SeedContentOptions, ServiceAccountFileConfig, ServiceManager, ServiceManagerOptions, ServiceState, SlackWorkspaceOptions, StatusHandler, StatusHandlerResult, StatusResponse, ToolDescriptor, ToolRegistrationOptions, ToolResult, UpdateManagedSectionOptions, VersionStamp, WorkspaceConfig, WriteComponentVersionOptions };
|
package/dist/index.js
CHANGED
|
@@ -183,14 +183,14 @@ const PLATFORM_COMPONENTS = [
|
|
|
183
183
|
* Core library version, inlined at build time.
|
|
184
184
|
*
|
|
185
185
|
* @remarks
|
|
186
|
-
* The `0.
|
|
186
|
+
* The `0.5.0` placeholder is replaced by
|
|
187
187
|
* `@rollup/plugin-replace` during the build with the actual version
|
|
188
188
|
* from `package.json`. This ensures the correct version survives
|
|
189
189
|
* when consumers bundle core into their own dist (where runtime
|
|
190
190
|
* `import.meta.url`-based resolution would find the wrong package.json).
|
|
191
191
|
*/
|
|
192
192
|
/** The core library version from package.json (inlined at build time). */
|
|
193
|
-
const CORE_VERSION = '0.
|
|
193
|
+
const CORE_VERSION = '0.5.0';
|
|
194
194
|
|
|
195
195
|
/**
|
|
196
196
|
* Workspace and config root initialization.
|
|
@@ -1007,7 +1007,7 @@ function parseHeartbeat(fileContent) {
|
|
|
1007
1007
|
const userContent = fileContent.slice(0, headingIndex).trim();
|
|
1008
1008
|
const sectionContent = fileContent.slice(headingIndex + HEARTBEAT_HEADING.length);
|
|
1009
1009
|
const entries = [];
|
|
1010
|
-
const h2Re = /^## (jeeves-\S
|
|
1010
|
+
const h2Re = /^## (jeeves-\S+?|MEMORY\.md)(?:: declined)?$/gm;
|
|
1011
1011
|
let match;
|
|
1012
1012
|
const h2Positions = [];
|
|
1013
1013
|
while ((match = h2Re.exec(sectionContent)) !== null) {
|
|
@@ -1477,6 +1477,12 @@ MEMORY.md has a character budget (default 20,000). Core tracks:
|
|
|
1477
1477
|
- Evergreen sections (no dates) are never flagged
|
|
1478
1478
|
|
|
1479
1479
|
Review is human/agent-mediated — core does not auto-delete.
|
|
1480
|
+
|
|
1481
|
+
### HEARTBEAT Integration
|
|
1482
|
+
|
|
1483
|
+
Memory hygiene is checked on every \`ComponentWriter\` cycle alongside component health. When budget or staleness thresholds are breached, a \`## MEMORY.md\` alert appears in HEARTBEAT.md under \`# Jeeves Platform Status\`. The alert includes character count, budget usage percentage, and any stale section names. When memory is healthy, the heading is absent — no alert content, no LLM cost on heartbeat polls.
|
|
1484
|
+
|
|
1485
|
+
The \`## MEMORY.md\` heading follows the same declined/active lifecycle as component headings (\`## jeeves-{name}\`). Users can decline memory alerts by changing the heading to \`## MEMORY.md: declined\`.
|
|
1480
1486
|
`;
|
|
1481
1487
|
|
|
1482
1488
|
/**
|
|
@@ -3520,6 +3526,132 @@ function scanAndEscalateCleanup(targets, gatewayUrl, pendingCleanups) {
|
|
|
3520
3526
|
}
|
|
3521
3527
|
}
|
|
3522
3528
|
|
|
3529
|
+
/**
|
|
3530
|
+
* Memory budget accounting and staleness detection for MEMORY.md.
|
|
3531
|
+
*
|
|
3532
|
+
* @remarks
|
|
3533
|
+
* Scans MEMORY.md for ISO date patterns in H2/H3 headings and bullet items.
|
|
3534
|
+
* Reports character count against a configured budget, warning threshold state,
|
|
3535
|
+
* and stale section candidates. Does not auto-delete: review remains
|
|
3536
|
+
* human- or agent-mediated (Decision 42).
|
|
3537
|
+
*/
|
|
3538
|
+
/** ISO date pattern: YYYY-MM-DD. */
|
|
3539
|
+
const ISO_DATE_RE = /\b(\d{4}-\d{2}-\d{2})\b/g;
|
|
3540
|
+
/** H2 heading pattern used to split sections. */
|
|
3541
|
+
const H2_RE = /^## /m;
|
|
3542
|
+
/**
|
|
3543
|
+
* Extract the most recent ISO date from a string.
|
|
3544
|
+
*
|
|
3545
|
+
* @param text - Text to scan for dates.
|
|
3546
|
+
* @returns The most recent date found, or undefined.
|
|
3547
|
+
*/
|
|
3548
|
+
function extractMostRecentDate(text) {
|
|
3549
|
+
const matches = text.match(ISO_DATE_RE);
|
|
3550
|
+
if (!matches)
|
|
3551
|
+
return undefined;
|
|
3552
|
+
let latest;
|
|
3553
|
+
for (const match of matches) {
|
|
3554
|
+
const d = new Date(match + 'T00:00:00Z');
|
|
3555
|
+
if (!Number.isNaN(d.getTime())) {
|
|
3556
|
+
if (!latest || d > latest)
|
|
3557
|
+
latest = d;
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
return latest;
|
|
3561
|
+
}
|
|
3562
|
+
/**
|
|
3563
|
+
* Analyze MEMORY.md for budget and staleness.
|
|
3564
|
+
*
|
|
3565
|
+
* @param options - Analysis configuration.
|
|
3566
|
+
* @returns Memory hygiene result.
|
|
3567
|
+
*/
|
|
3568
|
+
function analyzeMemory(options) {
|
|
3569
|
+
const { workspacePath, budget, warningThreshold, staleDays } = options;
|
|
3570
|
+
const memoryPath = join(workspacePath, WORKSPACE_FILES.memory);
|
|
3571
|
+
if (!existsSync(memoryPath)) {
|
|
3572
|
+
return {
|
|
3573
|
+
exists: false,
|
|
3574
|
+
charCount: 0,
|
|
3575
|
+
budget,
|
|
3576
|
+
usage: 0,
|
|
3577
|
+
warning: false,
|
|
3578
|
+
overBudget: false,
|
|
3579
|
+
staleCandidates: 0,
|
|
3580
|
+
staleSectionNames: [],
|
|
3581
|
+
};
|
|
3582
|
+
}
|
|
3583
|
+
const content = readFileSync(memoryPath, 'utf-8');
|
|
3584
|
+
const charCount = content.length;
|
|
3585
|
+
const usage = budget > 0 ? charCount / budget : charCount > 0 ? Infinity : 0;
|
|
3586
|
+
const warning = usage >= warningThreshold;
|
|
3587
|
+
const overBudget = usage > 1;
|
|
3588
|
+
// Split into H2 sections and scan for staleness
|
|
3589
|
+
const sections = content.split(H2_RE).slice(1); // skip content before first H2
|
|
3590
|
+
const now = Date.now();
|
|
3591
|
+
const thresholdMs = staleDays * 24 * 60 * 60 * 1000;
|
|
3592
|
+
const staleSectionNames = [];
|
|
3593
|
+
for (const section of sections) {
|
|
3594
|
+
const sectionName = section.split('\n')[0]?.trim() ?? '';
|
|
3595
|
+
const recentDate = extractMostRecentDate(section);
|
|
3596
|
+
// Sections without dates are evergreen — never flagged (Decision 47)
|
|
3597
|
+
if (!recentDate)
|
|
3598
|
+
continue;
|
|
3599
|
+
if (now - recentDate.getTime() > thresholdMs) {
|
|
3600
|
+
staleSectionNames.push(sectionName);
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
return {
|
|
3604
|
+
exists: true,
|
|
3605
|
+
charCount,
|
|
3606
|
+
budget,
|
|
3607
|
+
usage,
|
|
3608
|
+
warning,
|
|
3609
|
+
overBudget,
|
|
3610
|
+
staleCandidates: staleSectionNames.length,
|
|
3611
|
+
staleSectionNames,
|
|
3612
|
+
};
|
|
3613
|
+
}
|
|
3614
|
+
|
|
3615
|
+
/**
|
|
3616
|
+
* HEARTBEAT integration for memory hygiene.
|
|
3617
|
+
*
|
|
3618
|
+
* @remarks
|
|
3619
|
+
* Calls `analyzeMemory()` and converts the result into a `HeartbeatEntry`
|
|
3620
|
+
* suitable for inclusion in the HEARTBEAT.md platform status section.
|
|
3621
|
+
* Returns `undefined` when MEMORY.md is healthy (no alert needed).
|
|
3622
|
+
*
|
|
3623
|
+
* Uses the `## MEMORY.md` heading (Decision 50) to distinguish memory
|
|
3624
|
+
* alerts from component alerts (`## jeeves-{name}`).
|
|
3625
|
+
*/
|
|
3626
|
+
/** The HEARTBEAT heading name for memory alerts. */
|
|
3627
|
+
const MEMORY_HEARTBEAT_NAME = 'MEMORY.md';
|
|
3628
|
+
/**
|
|
3629
|
+
* Check memory health and return a HEARTBEAT entry if unhealthy.
|
|
3630
|
+
*
|
|
3631
|
+
* @param options - Memory hygiene options (workspacePath, budget, etc.).
|
|
3632
|
+
* @returns A `HeartbeatEntry` when memory needs attention, `undefined` when healthy.
|
|
3633
|
+
*/
|
|
3634
|
+
function checkMemoryHealth(options) {
|
|
3635
|
+
const result = analyzeMemory(options);
|
|
3636
|
+
if (!result.exists)
|
|
3637
|
+
return undefined;
|
|
3638
|
+
if (!result.warning && result.staleCandidates === 0)
|
|
3639
|
+
return undefined;
|
|
3640
|
+
const lines = [];
|
|
3641
|
+
if (result.warning) {
|
|
3642
|
+
const pct = Math.round(result.usage * 100);
|
|
3643
|
+
lines.push(`- Budget: ${result.charCount.toLocaleString()} / ${result.budget.toLocaleString()} chars (${String(pct)}%).${result.overBudget ? ' **Over budget.**' : ' Consider reviewing.'}`);
|
|
3644
|
+
}
|
|
3645
|
+
if (result.staleCandidates > 0) {
|
|
3646
|
+
lines.push(`- ${String(result.staleCandidates)} stale section${result.staleCandidates === 1 ? '' : 's'}: ${result.staleSectionNames.join(', ')}`);
|
|
3647
|
+
}
|
|
3648
|
+
return {
|
|
3649
|
+
name: MEMORY_HEARTBEAT_NAME,
|
|
3650
|
+
declined: false,
|
|
3651
|
+
content: lines.join('\n'),
|
|
3652
|
+
};
|
|
3653
|
+
}
|
|
3654
|
+
|
|
3523
3655
|
/**
|
|
3524
3656
|
* Core configuration schema and resolution.
|
|
3525
3657
|
*
|
|
@@ -4007,6 +4139,27 @@ async function runHeartbeatCycle(options) {
|
|
|
4007
4139
|
configRoot,
|
|
4008
4140
|
declinedNames,
|
|
4009
4141
|
});
|
|
4142
|
+
// Memory hygiene check (Decision 49)
|
|
4143
|
+
if (!declinedNames.has(MEMORY_HEARTBEAT_NAME)) {
|
|
4144
|
+
const wsConfig = loadWorkspaceConfig(workspacePath);
|
|
4145
|
+
const memoryEntry = checkMemoryHealth({
|
|
4146
|
+
workspacePath,
|
|
4147
|
+
budget: wsConfig?.memory?.budget ?? WORKSPACE_CONFIG_DEFAULTS.memory.budget,
|
|
4148
|
+
warningThreshold: wsConfig?.memory?.warningThreshold ??
|
|
4149
|
+
WORKSPACE_CONFIG_DEFAULTS.memory.warningThreshold,
|
|
4150
|
+
staleDays: wsConfig?.memory?.staleDays ??
|
|
4151
|
+
WORKSPACE_CONFIG_DEFAULTS.memory.staleDays,
|
|
4152
|
+
});
|
|
4153
|
+
if (memoryEntry)
|
|
4154
|
+
entries.push(memoryEntry);
|
|
4155
|
+
}
|
|
4156
|
+
else {
|
|
4157
|
+
entries.push({
|
|
4158
|
+
name: MEMORY_HEARTBEAT_NAME,
|
|
4159
|
+
declined: true,
|
|
4160
|
+
content: '',
|
|
4161
|
+
});
|
|
4162
|
+
}
|
|
4010
4163
|
await writeHeartbeatSection(heartbeatPath, entries);
|
|
4011
4164
|
}
|
|
4012
4165
|
catch (err) {
|
|
@@ -4242,92 +4395,6 @@ function getBindAddress(componentName) {
|
|
|
4242
4395
|
return DEFAULT_BIND_ADDRESS;
|
|
4243
4396
|
}
|
|
4244
4397
|
|
|
4245
|
-
/**
|
|
4246
|
-
* Memory budget accounting and staleness detection for MEMORY.md.
|
|
4247
|
-
*
|
|
4248
|
-
* @remarks
|
|
4249
|
-
* Scans MEMORY.md for ISO date patterns in H2/H3 headings and bullet items.
|
|
4250
|
-
* Reports character count against a configured budget, warning threshold state,
|
|
4251
|
-
* and stale section candidates. Does not auto-delete: review remains
|
|
4252
|
-
* human- or agent-mediated (Decision 42).
|
|
4253
|
-
*/
|
|
4254
|
-
/** ISO date pattern: YYYY-MM-DD. */
|
|
4255
|
-
const ISO_DATE_RE = /\b(\d{4}-\d{2}-\d{2})\b/g;
|
|
4256
|
-
/** H2 heading pattern used to split sections. */
|
|
4257
|
-
const H2_RE = /^## /m;
|
|
4258
|
-
/**
|
|
4259
|
-
* Extract the most recent ISO date from a string.
|
|
4260
|
-
*
|
|
4261
|
-
* @param text - Text to scan for dates.
|
|
4262
|
-
* @returns The most recent date found, or undefined.
|
|
4263
|
-
*/
|
|
4264
|
-
function extractMostRecentDate(text) {
|
|
4265
|
-
const matches = text.match(ISO_DATE_RE);
|
|
4266
|
-
if (!matches)
|
|
4267
|
-
return undefined;
|
|
4268
|
-
let latest;
|
|
4269
|
-
for (const match of matches) {
|
|
4270
|
-
const d = new Date(match + 'T00:00:00Z');
|
|
4271
|
-
if (!Number.isNaN(d.getTime())) {
|
|
4272
|
-
if (!latest || d > latest)
|
|
4273
|
-
latest = d;
|
|
4274
|
-
}
|
|
4275
|
-
}
|
|
4276
|
-
return latest;
|
|
4277
|
-
}
|
|
4278
|
-
/**
|
|
4279
|
-
* Analyze MEMORY.md for budget and staleness.
|
|
4280
|
-
*
|
|
4281
|
-
* @param options - Analysis configuration.
|
|
4282
|
-
* @returns Memory hygiene result.
|
|
4283
|
-
*/
|
|
4284
|
-
function analyzeMemory(options) {
|
|
4285
|
-
const { workspacePath, budget, warningThreshold, staleDays } = options;
|
|
4286
|
-
const memoryPath = join(workspacePath, WORKSPACE_FILES.memory);
|
|
4287
|
-
if (!existsSync(memoryPath)) {
|
|
4288
|
-
return {
|
|
4289
|
-
exists: false,
|
|
4290
|
-
charCount: 0,
|
|
4291
|
-
budget,
|
|
4292
|
-
usage: 0,
|
|
4293
|
-
warning: false,
|
|
4294
|
-
overBudget: false,
|
|
4295
|
-
staleCandidates: 0,
|
|
4296
|
-
staleSectionNames: [],
|
|
4297
|
-
};
|
|
4298
|
-
}
|
|
4299
|
-
const content = readFileSync(memoryPath, 'utf-8');
|
|
4300
|
-
const charCount = content.length;
|
|
4301
|
-
const usage = budget > 0 ? charCount / budget : charCount > 0 ? Infinity : 0;
|
|
4302
|
-
const warning = usage >= warningThreshold;
|
|
4303
|
-
const overBudget = usage > 1;
|
|
4304
|
-
// Split into H2 sections and scan for staleness
|
|
4305
|
-
const sections = content.split(H2_RE).slice(1); // skip content before first H2
|
|
4306
|
-
const now = Date.now();
|
|
4307
|
-
const thresholdMs = staleDays * 24 * 60 * 60 * 1000;
|
|
4308
|
-
const staleSectionNames = [];
|
|
4309
|
-
for (const section of sections) {
|
|
4310
|
-
const sectionName = section.split('\n')[0]?.trim() ?? '';
|
|
4311
|
-
const recentDate = extractMostRecentDate(section);
|
|
4312
|
-
// Sections without dates are evergreen — never flagged (Decision 47)
|
|
4313
|
-
if (!recentDate)
|
|
4314
|
-
continue;
|
|
4315
|
-
if (now - recentDate.getTime() > thresholdMs) {
|
|
4316
|
-
staleSectionNames.push(sectionName);
|
|
4317
|
-
}
|
|
4318
|
-
}
|
|
4319
|
-
return {
|
|
4320
|
-
exists: true,
|
|
4321
|
-
charCount,
|
|
4322
|
-
budget,
|
|
4323
|
-
usage,
|
|
4324
|
-
warning,
|
|
4325
|
-
overBudget,
|
|
4326
|
-
staleCandidates: staleSectionNames.length,
|
|
4327
|
-
staleSectionNames,
|
|
4328
|
-
};
|
|
4329
|
-
}
|
|
4330
|
-
|
|
4331
4398
|
/**
|
|
4332
4399
|
* One-shot content seeding used by the CLI install command.
|
|
4333
4400
|
*
|
|
@@ -5091,4 +5158,4 @@ async function getChannelWorkspace(channelId, token, options) {
|
|
|
5091
5158
|
return teamId;
|
|
5092
5159
|
}
|
|
5093
5160
|
|
|
5094
|
-
export { AGENTS_MARKERS, CLEANUP_FLAG, COMPONENT_CONFIG_PREFIX, COMPONENT_VERSIONS_FILE, CONFIG_FILE, CORE_CONFIG_DIR, CORE_VERSION, ComponentWriter, DEFAULT_BIND_ADDRESS, DEFAULT_CORE_VERSION, DEFAULT_PORTS, HEARTBEAT_HEADING, JEEVES_SKILL_DIR, META_PORT, PLATFORM_COMPONENTS, REGISTRY_CACHE_FILE, RUNNER_PORT, SECTION_IDS, SECTION_ORDER, SERVER_PORT, SKILLS_DIR, SOUL_MARKERS, STALENESS_THRESHOLD_MS, STALE_LOCK_MS, TEMPLATES_DIR, TOOLS_MARKERS, VERSION_STAMP_PATTERN, WATCHER_PORT, WORKSPACE_CONFIG_DEFAULTS, WORKSPACE_CONFIG_FILE, WORKSPACE_FILES, analyzeMemory, appendJsonl, atomicWrite, buildEffectiveConfig, buildHeartbeatSection, checkNodeVersion, checkRegistryVersion, connectionFail, coreConfigSchema, createAsyncContentCache, createComponentWriter, createConfigApplyHandler, createConfigQueryHandler, createGoogleAuth, createPluginCli, createPluginToolset, createServiceCli, createServiceManager, createStatusHandler, ensureDir, extractMostRecentDate, fail, fetchJson, fetchWithTimeout, formatBeginMarker, formatEndMarker, generateJsonSchema, generateWorkspaceJsonSchema, getArg, getBindAddress, getChannelWorkspace, getComponentConfigDir, getConfigRoot, getCoreConfigDir, getCoreConfigFile, getEffectiveServiceName, getPackageVersion, getServiceState, getServiceUrl, getWorkspacePath, init, isPrime, jaccard, jeevesComponentDescriptorSchema, loadEnvFile, loadWorkspaceConfig, needsCleanup, nowIso, ok, orchestrateHeartbeat, parseArgs, parseHeartbeat, parseManaged, patchConfig, postJson, readComponentVersions, readJson, readJsonl, refreshPlatformContent, removeComponentVersion, removeManagedSection, resetInit, resolveConfigPath, resolveConfigValue, resolveOpenClawHome, resolveOptionalPluginSetting, resolvePluginSetting, resolveWorkspacePath, run, runScript, runWithRetry, saveCache, seedContent, seedSkill, shingles, shouldWrite, sleepAsync, sleepMs, updateManagedSection, uuid, withFileLock, workspaceConfigSchema, writeComponentVersion, writeHeartbeatSection, writeJsonAtomic, writeJsonl };
|
|
5161
|
+
export { AGENTS_MARKERS, CLEANUP_FLAG, COMPONENT_CONFIG_PREFIX, COMPONENT_VERSIONS_FILE, CONFIG_FILE, CORE_CONFIG_DIR, CORE_VERSION, ComponentWriter, DEFAULT_BIND_ADDRESS, DEFAULT_CORE_VERSION, DEFAULT_PORTS, HEARTBEAT_HEADING, JEEVES_SKILL_DIR, MEMORY_HEARTBEAT_NAME, META_PORT, PLATFORM_COMPONENTS, REGISTRY_CACHE_FILE, RUNNER_PORT, SECTION_IDS, SECTION_ORDER, SERVER_PORT, SKILLS_DIR, SOUL_MARKERS, STALENESS_THRESHOLD_MS, STALE_LOCK_MS, TEMPLATES_DIR, TOOLS_MARKERS, VERSION_STAMP_PATTERN, WATCHER_PORT, WORKSPACE_CONFIG_DEFAULTS, WORKSPACE_CONFIG_FILE, WORKSPACE_FILES, analyzeMemory, appendJsonl, atomicWrite, buildEffectiveConfig, buildHeartbeatSection, checkMemoryHealth, checkNodeVersion, checkRegistryVersion, connectionFail, coreConfigSchema, createAsyncContentCache, createComponentWriter, createConfigApplyHandler, createConfigQueryHandler, createGoogleAuth, createPluginCli, createPluginToolset, createServiceCli, createServiceManager, createStatusHandler, ensureDir, extractMostRecentDate, fail, fetchJson, fetchWithTimeout, formatBeginMarker, formatEndMarker, generateJsonSchema, generateWorkspaceJsonSchema, getArg, getBindAddress, getChannelWorkspace, getComponentConfigDir, getConfigRoot, getCoreConfigDir, getCoreConfigFile, getEffectiveServiceName, getPackageVersion, getServiceState, getServiceUrl, getWorkspacePath, init, isPrime, jaccard, jeevesComponentDescriptorSchema, loadEnvFile, loadWorkspaceConfig, needsCleanup, nowIso, ok, orchestrateHeartbeat, parseArgs, parseHeartbeat, parseManaged, patchConfig, postJson, readComponentVersions, readJson, readJsonl, refreshPlatformContent, removeComponentVersion, removeManagedSection, resetInit, resolveConfigPath, resolveConfigValue, resolveOpenClawHome, resolveOptionalPluginSetting, resolvePluginSetting, resolveWorkspacePath, run, runScript, runWithRetry, saveCache, seedContent, seedSkill, shingles, shouldWrite, sleepAsync, sleepMs, updateManagedSection, uuid, withFileLock, workspaceConfigSchema, writeComponentVersion, writeHeartbeatSection, writeJsonAtomic, writeJsonl };
|
package/package.json
CHANGED