@psiclawops/hypermem 0.6.2 → 0.8.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/ARCHITECTURE.md +31 -39
- package/README.md +20 -14
- package/bin/hypermem-status.mjs +1 -1
- package/dist/background-indexer.d.ts +14 -3
- package/dist/background-indexer.d.ts.map +1 -1
- package/dist/background-indexer.js +135 -27
- package/dist/budget-policy.d.ts +22 -0
- package/dist/budget-policy.d.ts.map +1 -0
- package/dist/budget-policy.js +27 -0
- package/dist/cache.d.ts +11 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/compositor-utils.d.ts +31 -0
- package/dist/compositor-utils.d.ts.map +1 -0
- package/dist/compositor-utils.js +47 -0
- package/dist/compositor.d.ts +163 -1
- package/dist/compositor.d.ts.map +1 -1
- package/dist/compositor.js +862 -130
- package/dist/content-hash.d.ts +43 -0
- package/dist/content-hash.d.ts.map +1 -0
- package/dist/content-hash.js +75 -0
- package/dist/context-store.d.ts +54 -0
- package/dist/context-store.d.ts.map +1 -1
- package/dist/context-store.js +102 -0
- package/dist/contradiction-audit-store.d.ts +54 -0
- package/dist/contradiction-audit-store.d.ts.map +1 -0
- package/dist/contradiction-audit-store.js +88 -0
- package/dist/contradiction-detector.d.ts +78 -0
- package/dist/contradiction-detector.d.ts.map +1 -0
- package/dist/contradiction-detector.js +362 -0
- package/dist/contradiction-resolution-policy.d.ts +21 -0
- package/dist/contradiction-resolution-policy.d.ts.map +1 -0
- package/dist/contradiction-resolution-policy.js +17 -0
- package/dist/cross-agent.d.ts +1 -1
- package/dist/cross-agent.js +17 -17
- package/dist/degradation.d.ts +102 -0
- package/dist/degradation.d.ts.map +1 -0
- package/dist/degradation.js +141 -0
- package/dist/dreaming-promoter.d.ts +39 -1
- package/dist/dreaming-promoter.d.ts.map +1 -1
- package/dist/dreaming-promoter.js +70 -4
- package/dist/expertise-store.d.ts +129 -0
- package/dist/expertise-store.d.ts.map +1 -0
- package/dist/expertise-store.js +342 -0
- package/dist/fact-store.d.ts +15 -0
- package/dist/fact-store.d.ts.map +1 -1
- package/dist/fact-store.js +52 -5
- package/dist/index.d.ts +74 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +407 -29
- package/dist/knowledge-lint.d.ts +2 -0
- package/dist/knowledge-lint.d.ts.map +1 -1
- package/dist/knowledge-lint.js +40 -1
- package/dist/library-schema.d.ts +7 -2
- package/dist/library-schema.d.ts.map +1 -1
- package/dist/library-schema.js +307 -2
- package/dist/message-store.d.ts +64 -1
- package/dist/message-store.d.ts.map +1 -1
- package/dist/message-store.js +137 -1
- package/dist/proactive-pass.d.ts +2 -2
- package/dist/proactive-pass.d.ts.map +1 -1
- package/dist/proactive-pass.js +66 -12
- package/dist/replay-recovery.d.ts +29 -0
- package/dist/replay-recovery.d.ts.map +1 -0
- package/dist/replay-recovery.js +82 -0
- package/dist/reranker.d.ts +95 -0
- package/dist/reranker.d.ts.map +1 -0
- package/dist/reranker.js +308 -0
- package/dist/schema.d.ts +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +46 -1
- package/dist/seed.d.ts +1 -1
- package/dist/seed.js +1 -1
- package/dist/session-flusher.d.ts +4 -4
- package/dist/session-flusher.d.ts.map +1 -1
- package/dist/session-flusher.js +3 -3
- package/dist/spawn-context.d.ts +1 -1
- package/dist/spawn-context.js +1 -1
- package/dist/temporal-store.d.ts +1 -0
- package/dist/temporal-store.d.ts.map +1 -1
- package/dist/tool-artifact-store.d.ts +98 -0
- package/dist/tool-artifact-store.d.ts.map +1 -0
- package/dist/tool-artifact-store.js +244 -0
- package/dist/topic-detector.js +2 -2
- package/dist/topic-store.d.ts +6 -0
- package/dist/topic-store.d.ts.map +1 -1
- package/dist/topic-store.js +39 -0
- package/dist/topic-synthesizer.js +1 -1
- package/dist/trigger-registry.d.ts +1 -1
- package/dist/trigger-registry.js +4 -4
- package/dist/types.d.ts +239 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/vector-store.d.ts +2 -1
- package/dist/vector-store.d.ts.map +1 -1
- package/dist/vector-store.js +3 -0
- package/dist/version.d.ts +10 -10
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +10 -10
- package/package.json +6 -4
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HyperMem Canonical Degradation Contracts, Phase C0.2
|
|
3
|
+
*
|
|
4
|
+
* Defines the typed surfaces, reason enum, and format builders for degraded
|
|
5
|
+
* prompt-visible outputs. These shapes stay in volatile context and should not
|
|
6
|
+
* cross the stable-prefix boundary.
|
|
7
|
+
*/
|
|
8
|
+
/** All valid DegradationReason values as a readonly array. */
|
|
9
|
+
export const DEGRADATION_REASONS = [
|
|
10
|
+
'gradient_t2_prose',
|
|
11
|
+
'gradient_t3_stub',
|
|
12
|
+
'eviction_oversize',
|
|
13
|
+
'eviction_turn0_trim',
|
|
14
|
+
'wave_guard_pressure_high',
|
|
15
|
+
'wave_guard_pressure_elevated',
|
|
16
|
+
'budget_cluster_drop',
|
|
17
|
+
'artifact_oversize',
|
|
18
|
+
'artifact_fetch_hint',
|
|
19
|
+
'replay_cold_redis',
|
|
20
|
+
'replay_stabilizing',
|
|
21
|
+
'replay_exited',
|
|
22
|
+
'pressure_mismatch',
|
|
23
|
+
'unknown',
|
|
24
|
+
];
|
|
25
|
+
/** Field-length caps for canonical degraded strings. */
|
|
26
|
+
export const DEGRADATION_LIMITS = {
|
|
27
|
+
toolName: 64,
|
|
28
|
+
toolId: 64,
|
|
29
|
+
reason: 48,
|
|
30
|
+
toolSummary: 120,
|
|
31
|
+
toolArtifactId: 64,
|
|
32
|
+
artifactId: 64,
|
|
33
|
+
artifactPath: 160,
|
|
34
|
+
artifactFetchHint: 80,
|
|
35
|
+
replayState: 32,
|
|
36
|
+
replaySummary: 120,
|
|
37
|
+
};
|
|
38
|
+
function sanitizeInline(value) {
|
|
39
|
+
return value
|
|
40
|
+
.replace(/[\r\n\t]+/g, ' ')
|
|
41
|
+
.replace(/\s+/g, ' ')
|
|
42
|
+
.replace(/]/g, ')')
|
|
43
|
+
.trim();
|
|
44
|
+
}
|
|
45
|
+
function clampInline(value, limit) {
|
|
46
|
+
const cleaned = sanitizeInline(value);
|
|
47
|
+
if (cleaned.length <= limit)
|
|
48
|
+
return cleaned;
|
|
49
|
+
if (limit <= 3)
|
|
50
|
+
return cleaned.slice(0, limit);
|
|
51
|
+
return cleaned.slice(0, limit - 3) + '...';
|
|
52
|
+
}
|
|
53
|
+
export function isDegradationReason(value) {
|
|
54
|
+
return DEGRADATION_REASONS.includes(value);
|
|
55
|
+
}
|
|
56
|
+
// Matches stubs with or without the optional artifact field.
|
|
57
|
+
const TOOL_CHAIN_RE = /^\[tool:([^\s\]]+) id=([^\s\]]+) status=(ejected) reason=([^\s\]]+)(?: artifact=([^\s\]]+))? summary=(.*)\]$/s;
|
|
58
|
+
export function formatToolChainStub(stub) {
|
|
59
|
+
const name = clampInline(stub.name, DEGRADATION_LIMITS.toolName);
|
|
60
|
+
const id = clampInline(stub.id, DEGRADATION_LIMITS.toolId);
|
|
61
|
+
const reason = clampInline(stub.reason, DEGRADATION_LIMITS.reason);
|
|
62
|
+
const summary = clampInline(stub.summary, DEGRADATION_LIMITS.toolSummary);
|
|
63
|
+
const artifactPart = stub.artifactId
|
|
64
|
+
? ` artifact=${clampInline(stub.artifactId, DEGRADATION_LIMITS.toolArtifactId)}`
|
|
65
|
+
: '';
|
|
66
|
+
return `[tool:${name} id=${id} status=${stub.status} reason=${reason}${artifactPart} summary=${summary}]`;
|
|
67
|
+
}
|
|
68
|
+
export function parseToolChainStub(text) {
|
|
69
|
+
const match = text.match(TOOL_CHAIN_RE);
|
|
70
|
+
if (!match)
|
|
71
|
+
return null;
|
|
72
|
+
const [, name, id, status, reason, artifactId, summary] = match;
|
|
73
|
+
if (status !== 'ejected' || !isDegradationReason(reason))
|
|
74
|
+
return null;
|
|
75
|
+
const out = { name, id, status: 'ejected', reason, summary };
|
|
76
|
+
if (artifactId)
|
|
77
|
+
out.artifactId = artifactId;
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
export function isToolChainStub(text) {
|
|
81
|
+
return parseToolChainStub(text) !== null;
|
|
82
|
+
}
|
|
83
|
+
const ARTIFACT_REF_RE = /^\[artifact:(.+?) path=(.+?) size=(\d+) status=(degraded) reason=(.+?) fetch=(.*)\]$/s;
|
|
84
|
+
export function formatArtifactRef(ref) {
|
|
85
|
+
const id = clampInline(ref.id, DEGRADATION_LIMITS.artifactId);
|
|
86
|
+
const path = clampInline(ref.path, DEGRADATION_LIMITS.artifactPath);
|
|
87
|
+
const reason = clampInline(ref.reason, DEGRADATION_LIMITS.reason);
|
|
88
|
+
const fetchHint = clampInline(ref.fetchHint, DEGRADATION_LIMITS.artifactFetchHint);
|
|
89
|
+
return `[artifact:${id} path=${path} size=${ref.sizeTokens} status=${ref.status} reason=${reason} fetch=${fetchHint}]`;
|
|
90
|
+
}
|
|
91
|
+
export function parseArtifactRef(text) {
|
|
92
|
+
const match = text.match(ARTIFACT_REF_RE);
|
|
93
|
+
if (!match)
|
|
94
|
+
return null;
|
|
95
|
+
const [, id, path, sizeTokens, status, reason, fetchHint] = match;
|
|
96
|
+
if (status !== 'degraded' || !isDegradationReason(reason))
|
|
97
|
+
return null;
|
|
98
|
+
return {
|
|
99
|
+
id,
|
|
100
|
+
path,
|
|
101
|
+
sizeTokens: parseInt(sizeTokens, 10),
|
|
102
|
+
status: 'degraded',
|
|
103
|
+
reason,
|
|
104
|
+
fetchHint,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
export function isArtifactRef(text) {
|
|
108
|
+
return parseArtifactRef(text) !== null;
|
|
109
|
+
}
|
|
110
|
+
export function isReplayState(value) {
|
|
111
|
+
return value === 'entering' || value === 'stabilizing' || value === 'exited';
|
|
112
|
+
}
|
|
113
|
+
const REPLAY_MARKER_RE = /^\[replay state=([^\s\]]+) status=(bounded) reason=([^\s\]]+) summary=(.*)\]$/s;
|
|
114
|
+
export function formatReplayMarker(marker) {
|
|
115
|
+
const state = clampInline(marker.state, DEGRADATION_LIMITS.replayState);
|
|
116
|
+
const reason = clampInline(marker.reason, DEGRADATION_LIMITS.reason);
|
|
117
|
+
const summary = clampInline(marker.summary, DEGRADATION_LIMITS.replaySummary);
|
|
118
|
+
return `[replay state=${state} status=${marker.status} reason=${reason} summary=${summary}]`;
|
|
119
|
+
}
|
|
120
|
+
export function parseReplayMarker(text) {
|
|
121
|
+
const match = text.match(REPLAY_MARKER_RE);
|
|
122
|
+
if (!match)
|
|
123
|
+
return null;
|
|
124
|
+
const [, state, status, reason, summary] = match;
|
|
125
|
+
if (status !== 'bounded' || !isReplayState(state) || !isDegradationReason(reason))
|
|
126
|
+
return null;
|
|
127
|
+
return {
|
|
128
|
+
state,
|
|
129
|
+
status: 'bounded',
|
|
130
|
+
reason,
|
|
131
|
+
summary,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
export function isReplayMarker(text) {
|
|
135
|
+
return parseReplayMarker(text) !== null;
|
|
136
|
+
}
|
|
137
|
+
// ─── Shared helpers ───────────────────────────────────────────────────────────
|
|
138
|
+
export function isDegradedContent(text) {
|
|
139
|
+
return isToolChainStub(text) || isArtifactRef(text) || isReplayMarker(text);
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=degradation.js.map
|
|
@@ -67,10 +67,48 @@ export interface DreamerResult {
|
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
69
|
* Resolve the workspace directory for an agent.
|
|
70
|
-
* Council agents live at ~/.openclaw/workspace
|
|
70
|
+
* Council agents live at ~/.openclaw/workspace/{agentId}/
|
|
71
71
|
* Other agents at ~/.openclaw/workspace/{agentId}/
|
|
72
72
|
*/
|
|
73
73
|
export declare function resolveAgentWorkspacePath(agentId: string): Promise<string | null>;
|
|
74
|
+
/**
|
|
75
|
+
* Temporal-state markers that indicate a fact is time-bound or conditional.
|
|
76
|
+
*
|
|
77
|
+
* Facts containing any of these markers MUST carry structured recency metadata
|
|
78
|
+
* (`validFrom` / `invalidAt` columns) to be eligible for durable promotion.
|
|
79
|
+
* Plain ISO dates in the content text do NOT satisfy this requirement — a
|
|
80
|
+
* dated sentence like "suspended pending X as of 2026-04-18" is still temporary
|
|
81
|
+
* and would harden stale state if promoted.
|
|
82
|
+
*
|
|
83
|
+
* Exported so tests and downstream callers can verify coverage and extend it.
|
|
84
|
+
* Keep this list centralized; do not fork copies into other modules.
|
|
85
|
+
*/
|
|
86
|
+
export declare const TEMPORAL_MARKERS: RegExp[];
|
|
87
|
+
/**
|
|
88
|
+
* Returns true if content contains any temporal-state marker.
|
|
89
|
+
* Exported for test coverage and for callers that want to gate their own writes.
|
|
90
|
+
*/
|
|
91
|
+
export declare function hasTemporalMarker(content: string): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Structured recency metadata from the facts table.
|
|
94
|
+
* Either field being set (non-null, non-empty) signals the fact row was
|
|
95
|
+
* authored with temporal bounds the store can enforce.
|
|
96
|
+
*/
|
|
97
|
+
export interface FactRecencyMeta {
|
|
98
|
+
validFrom?: string | null;
|
|
99
|
+
invalidAt?: string | null;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Reject facts that are clearly noise at promotion time.
|
|
103
|
+
* A second line of defense — the indexer's isQualityFact() is the primary filter,
|
|
104
|
+
* but legacy noise in the DB (pre-TUNE-013) still needs to be caught here.
|
|
105
|
+
*
|
|
106
|
+
* Additionally blocks promotion of temporally-scoped facts that lack structured
|
|
107
|
+
* recency metadata. A fact like "model frozen until provider routing stable"
|
|
108
|
+
* without `validFrom`/`invalidAt` would harden a temporary state into durable
|
|
109
|
+
* memory forever. Plain ISO dates in content text do NOT bypass this check.
|
|
110
|
+
*/
|
|
111
|
+
export declare function isPromotable(content: string, meta?: FactRecencyMeta): boolean;
|
|
74
112
|
/**
|
|
75
113
|
* Run the dreaming promotion pass for a single agent.
|
|
76
114
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dreaming-promoter.d.ts","sourceRoot":"","sources":["../src/dreaming-promoter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mFAAmF;IACnF,YAAY,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,MAAM,EAAE,OAAO,CAAC;IAChB,+EAA+E;IAC/E,mBAAmB,EAAE,MAAM,CAAC;IAC5B,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,sBAAsB,EAAE,aASpC,CAAC;AAIF,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;CACjB;AAID;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBvF;
|
|
1
|
+
{"version":3,"file":"dreaming-promoter.d.ts","sourceRoot":"","sources":["../src/dreaming-promoter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mFAAmF;IACnF,YAAY,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,MAAM,EAAE,OAAO,CAAC;IAChB,+EAA+E;IAC/E,mBAAmB,EAAE,MAAM,CAAC;IAC5B,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,sBAAsB,EAAE,aASpC,CAAC;AAIF,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;CACjB;AAID;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBvF;AAoKD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,EAyBpC,CAAC;AAEF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AASD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,eAAe,GAAG,OAAO,CAsD7E;AAGD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,YAAY,EACvB,MAAM,GAAE,OAAO,CAAC,aAAa,CAAM,GAClC,OAAO,CAAC,aAAa,CAAC,CAoJxB;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAAE,EAClB,SAAS,EAAE,YAAY,EACvB,MAAM,GAAE,OAAO,CAAC,aAAa,CAAM,GAClC,OAAO,CAAC,aAAa,EAAE,CAAC,CAa1B"}
|
|
@@ -31,12 +31,12 @@ export const DEFAULT_DREAMER_CONFIG = {
|
|
|
31
31
|
// ─── Workspace path resolution ───────────────────────────────────────────────
|
|
32
32
|
/**
|
|
33
33
|
* Resolve the workspace directory for an agent.
|
|
34
|
-
* Council agents live at ~/.openclaw/workspace
|
|
34
|
+
* Council agents live at ~/.openclaw/workspace/{agentId}/
|
|
35
35
|
* Other agents at ~/.openclaw/workspace/{agentId}/
|
|
36
36
|
*/
|
|
37
37
|
export async function resolveAgentWorkspacePath(agentId) {
|
|
38
38
|
const home = os.homedir();
|
|
39
|
-
const councilPath = path.join(home, '.openclaw', 'workspace
|
|
39
|
+
const councilPath = path.join(home, '.openclaw', 'workspace', agentId);
|
|
40
40
|
const workspacePath = path.join(home, '.openclaw', 'workspace', agentId);
|
|
41
41
|
try {
|
|
42
42
|
await fs.access(councilPath);
|
|
@@ -185,12 +185,69 @@ async function appendToMemoryFile(memoryPath, entries) {
|
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
// ─── Promotion-time content filter ─────────────────────────────────────────
|
|
188
|
+
/**
|
|
189
|
+
* Temporal-state markers that indicate a fact is time-bound or conditional.
|
|
190
|
+
*
|
|
191
|
+
* Facts containing any of these markers MUST carry structured recency metadata
|
|
192
|
+
* (`validFrom` / `invalidAt` columns) to be eligible for durable promotion.
|
|
193
|
+
* Plain ISO dates in the content text do NOT satisfy this requirement — a
|
|
194
|
+
* dated sentence like "suspended pending X as of 2026-04-18" is still temporary
|
|
195
|
+
* and would harden stale state if promoted.
|
|
196
|
+
*
|
|
197
|
+
* Exported so tests and downstream callers can verify coverage and extend it.
|
|
198
|
+
* Keep this list centralized; do not fork copies into other modules.
|
|
199
|
+
*/
|
|
200
|
+
export const TEMPORAL_MARKERS = [
|
|
201
|
+
/\bas of\b/i,
|
|
202
|
+
/\buntil\b/i,
|
|
203
|
+
/\bcurrently\b/i,
|
|
204
|
+
/\bfor now\b/i,
|
|
205
|
+
/\bsuspended\b/i,
|
|
206
|
+
/\bpending\b/i,
|
|
207
|
+
/\brollout\b/i,
|
|
208
|
+
/\bphase\b/i,
|
|
209
|
+
/\btemporary\b/i,
|
|
210
|
+
/\btemporarily\b/i,
|
|
211
|
+
/\brecheck\b/i,
|
|
212
|
+
/\bpaused\b/i,
|
|
213
|
+
/\bblocked\b/i,
|
|
214
|
+
/\btrial\b/i,
|
|
215
|
+
/\bexperiment(?:al)?\b/i,
|
|
216
|
+
/\bexploratory period\b/i,
|
|
217
|
+
/\bin effect during\b/i,
|
|
218
|
+
/\bwhile .* (?:continues|ongoing|in progress|rolls out|rolling out)\b/i,
|
|
219
|
+
/\boverride\b/i,
|
|
220
|
+
/\bhotfix\b/i,
|
|
221
|
+
/\bworkaround\b/i,
|
|
222
|
+
/\bmigration (?:ongoing|in progress|underway)\b/i,
|
|
223
|
+
/\bfreeze(?:d)?\b/i,
|
|
224
|
+
/\bpre-release\b/i,
|
|
225
|
+
];
|
|
226
|
+
/**
|
|
227
|
+
* Returns true if content contains any temporal-state marker.
|
|
228
|
+
* Exported for test coverage and for callers that want to gate their own writes.
|
|
229
|
+
*/
|
|
230
|
+
export function hasTemporalMarker(content) {
|
|
231
|
+
return TEMPORAL_MARKERS.some((re) => re.test(content));
|
|
232
|
+
}
|
|
233
|
+
function hasStrongRecencyMetadata(meta) {
|
|
234
|
+
if (!meta)
|
|
235
|
+
return false;
|
|
236
|
+
const vf = (meta.validFrom ?? '').trim();
|
|
237
|
+
const ia = (meta.invalidAt ?? '').trim();
|
|
238
|
+
return vf.length > 0 || ia.length > 0;
|
|
239
|
+
}
|
|
188
240
|
/**
|
|
189
241
|
* Reject facts that are clearly noise at promotion time.
|
|
190
242
|
* A second line of defense — the indexer's isQualityFact() is the primary filter,
|
|
191
243
|
* but legacy noise in the DB (pre-TUNE-013) still needs to be caught here.
|
|
244
|
+
*
|
|
245
|
+
* Additionally blocks promotion of temporally-scoped facts that lack structured
|
|
246
|
+
* recency metadata. A fact like "model frozen until provider routing stable"
|
|
247
|
+
* without `validFrom`/`invalidAt` would harden a temporary state into durable
|
|
248
|
+
* memory forever. Plain ISO dates in content text do NOT bypass this check.
|
|
192
249
|
*/
|
|
193
|
-
function isPromotable(content) {
|
|
250
|
+
export function isPromotable(content, meta) {
|
|
194
251
|
// Multi-line content — reject both actual newlines AND escaped \n sequences
|
|
195
252
|
// (some facts stored pre-TUNE-013 have literal \n in the string value)
|
|
196
253
|
if (content.includes('\n') || content.includes('\\n'))
|
|
@@ -236,6 +293,13 @@ function isPromotable(content) {
|
|
|
236
293
|
// Minimum meaningful length: 50 chars as promotion floor (stricter than indexer's 40)
|
|
237
294
|
if (content.trim().length < 50)
|
|
238
295
|
return false;
|
|
296
|
+
// Temporal-state screen: if the content is time-bound ("until X", "suspended
|
|
297
|
+
// pending Y", "rollout phase", etc.) it must carry structured recency
|
|
298
|
+
// metadata. Otherwise the promoter would harden a temporary state into
|
|
299
|
+
// durable memory. Plain ISO dates in the content do NOT satisfy this — a
|
|
300
|
+
// dated sentence confirms the claim is temporary, it does not unblock it.
|
|
301
|
+
if (hasTemporalMarker(content) && !hasStrongRecencyMetadata(meta))
|
|
302
|
+
return false;
|
|
239
303
|
return true;
|
|
240
304
|
}
|
|
241
305
|
/**
|
|
@@ -284,6 +348,8 @@ export async function runDreamingPromoter(agentId, libraryDb, config = {}) {
|
|
|
284
348
|
content,
|
|
285
349
|
confidence,
|
|
286
350
|
decay_score,
|
|
351
|
+
valid_from,
|
|
352
|
+
invalid_at,
|
|
287
353
|
ROUND((julianday('now') - julianday(created_at)), 2) AS age_days
|
|
288
354
|
FROM facts
|
|
289
355
|
WHERE agent_id = ?
|
|
@@ -299,7 +365,7 @@ export async function runDreamingPromoter(agentId, libraryDb, config = {}) {
|
|
|
299
365
|
result.candidates = rawFacts.length;
|
|
300
366
|
// 4. Score and rank
|
|
301
367
|
const scored = rawFacts
|
|
302
|
-
.filter(f => isPromotable(f.content))
|
|
368
|
+
.filter(f => isPromotable(f.content, { validFrom: f.valid_from, invalidAt: f.invalid_at }))
|
|
303
369
|
.map(f => ({
|
|
304
370
|
id: f.id,
|
|
305
371
|
agentId: f.agent_id,
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem Expertise Store
|
|
3
|
+
*
|
|
4
|
+
* Stores domain expertise patterns — learned behaviors that make the Nth run
|
|
5
|
+
* better than the 1st. Two-phase lifecycle:
|
|
6
|
+
* 1. Observations: raw learnings logged from conversations, pipelines, reviews
|
|
7
|
+
* 2. Patterns: graduated observations with N≥3 confirming instances
|
|
8
|
+
*
|
|
9
|
+
* Patterns are agent-scoped but domain-tagged, enabling cross-agent queries.
|
|
10
|
+
* Patterns have confidence, frequency tracking, and decay on counter-evidence.
|
|
11
|
+
*/
|
|
12
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
13
|
+
export interface ExpertiseObservation {
|
|
14
|
+
id: number;
|
|
15
|
+
agentId: string;
|
|
16
|
+
domain: string;
|
|
17
|
+
context: string | null;
|
|
18
|
+
observationText: string;
|
|
19
|
+
sourceType: 'conversation' | 'pipeline' | 'review' | 'manual';
|
|
20
|
+
sourceRef: string | null;
|
|
21
|
+
createdAt: string;
|
|
22
|
+
}
|
|
23
|
+
export interface ExpertisePattern {
|
|
24
|
+
id: number;
|
|
25
|
+
agentId: string;
|
|
26
|
+
domain: string;
|
|
27
|
+
patternText: string;
|
|
28
|
+
confidence: number;
|
|
29
|
+
frequency: number;
|
|
30
|
+
firstSeen: string;
|
|
31
|
+
lastConfirmed: string;
|
|
32
|
+
invalidatedAt: string | null;
|
|
33
|
+
invalidationReason: string | null;
|
|
34
|
+
decayScore: number;
|
|
35
|
+
}
|
|
36
|
+
export interface ExpertiseEvidence {
|
|
37
|
+
observationId: number;
|
|
38
|
+
patternId: number;
|
|
39
|
+
relationship: 'confirms' | 'contradicts';
|
|
40
|
+
createdAt: string;
|
|
41
|
+
}
|
|
42
|
+
export declare class ExpertiseStore {
|
|
43
|
+
private readonly db;
|
|
44
|
+
private readonly graduationThreshold;
|
|
45
|
+
constructor(db: DatabaseSync, graduationThreshold?: number);
|
|
46
|
+
/**
|
|
47
|
+
* Record a raw observation from any source.
|
|
48
|
+
*/
|
|
49
|
+
record(agentId: string, observationText: string, domain: string, opts?: {
|
|
50
|
+
context?: string;
|
|
51
|
+
sourceType?: ExpertiseObservation['sourceType'];
|
|
52
|
+
sourceRef?: string;
|
|
53
|
+
}): ExpertiseObservation;
|
|
54
|
+
/**
|
|
55
|
+
* Get observations for an agent, optionally filtered by domain.
|
|
56
|
+
*/
|
|
57
|
+
getObservations(agentId: string, opts?: {
|
|
58
|
+
domain?: string;
|
|
59
|
+
limit?: number;
|
|
60
|
+
}): ExpertiseObservation[];
|
|
61
|
+
/**
|
|
62
|
+
* Retrieve active expertise patterns for current work context.
|
|
63
|
+
* Returns patterns sorted by confidence DESC, frequency DESC.
|
|
64
|
+
* Excludes invalidated patterns by default.
|
|
65
|
+
*/
|
|
66
|
+
query(agentId: string, domain: string, opts?: {
|
|
67
|
+
context?: string;
|
|
68
|
+
includeInvalidated?: boolean;
|
|
69
|
+
limit?: number;
|
|
70
|
+
minConfidence?: number;
|
|
71
|
+
}): ExpertisePattern[];
|
|
72
|
+
/**
|
|
73
|
+
* Cross-agent query: get patterns from any agent in a given domain.
|
|
74
|
+
* Useful for fleet-wide expertise ("what has any agent learned about X?").
|
|
75
|
+
*/
|
|
76
|
+
queryFleet(domain: string, opts?: {
|
|
77
|
+
limit?: number;
|
|
78
|
+
minConfidence?: number;
|
|
79
|
+
}): ExpertisePattern[];
|
|
80
|
+
/**
|
|
81
|
+
* Graduate an observation to a pattern.
|
|
82
|
+
*
|
|
83
|
+
* If a similar pattern already exists (same agent, domain, and pattern text prefix match),
|
|
84
|
+
* increments its frequency and updates lastConfirmed instead of creating a duplicate.
|
|
85
|
+
*
|
|
86
|
+
* Auto-graduation happens when an observation has N≥graduationThreshold confirming
|
|
87
|
+
* evidence links. Can also be called manually.
|
|
88
|
+
*/
|
|
89
|
+
graduate(agentId: string, observationId: number, opts?: {
|
|
90
|
+
patternText?: string;
|
|
91
|
+
confidence?: number;
|
|
92
|
+
}): ExpertisePattern | null;
|
|
93
|
+
/**
|
|
94
|
+
* Record evidence linking an observation to a pattern.
|
|
95
|
+
* If this pushes a pattern's contradicting evidence past threshold,
|
|
96
|
+
* auto-invalidates the pattern.
|
|
97
|
+
*/
|
|
98
|
+
addEvidence(observationId: number, patternId: number, relationship: 'confirms' | 'contradicts'): void;
|
|
99
|
+
/**
|
|
100
|
+
* Check if any observations are ready for auto-graduation.
|
|
101
|
+
* An observation graduates when it has N≥threshold confirming evidence links.
|
|
102
|
+
* Returns the number of newly graduated patterns.
|
|
103
|
+
*/
|
|
104
|
+
autoGraduate(agentId: string): number;
|
|
105
|
+
/**
|
|
106
|
+
* Mark a pattern as invalidated.
|
|
107
|
+
*/
|
|
108
|
+
invalidate(patternId: number, reason: string): boolean;
|
|
109
|
+
/**
|
|
110
|
+
* List all active patterns for an agent, optionally filtered by domain.
|
|
111
|
+
*/
|
|
112
|
+
list(agentId: string, opts?: {
|
|
113
|
+
domain?: string;
|
|
114
|
+
includeInvalidated?: boolean;
|
|
115
|
+
}): ExpertisePattern[];
|
|
116
|
+
/**
|
|
117
|
+
* Decay all patterns by a fixed rate. Similar to fact decay.
|
|
118
|
+
*/
|
|
119
|
+
decayPatterns(agentId: string, decayRate?: number): number;
|
|
120
|
+
/**
|
|
121
|
+
* Get pattern and observation counts for an agent.
|
|
122
|
+
*/
|
|
123
|
+
getStats(agentId: string): {
|
|
124
|
+
observations: number;
|
|
125
|
+
activePatterns: number;
|
|
126
|
+
invalidatedPatterns: number;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=expertise-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expertise-store.d.ts","sourceRoot":"","sources":["../src/expertise-store.ts"],"names":[],"mappings":"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"}
|