opencode-wait-a-minute 0.0.0-stage → 1.1.0-rc.10
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/LICENSE +21 -0
- package/README.md +318 -2
- package/index.js +1836 -0
- package/package.json +70 -4
- package/skills/codebase-design/SKILL.md +114 -0
- package/skills/design-contract/SKILL.md +27 -0
- package/skills/design-critique/SKILL.md +19 -0
- package/skills/design-system/SKILL.md +17 -0
- package/skills/design-system/references/image-analysis.md +14 -0
- package/skills/design-system/references/tokens.md +32 -0
- package/skills/design-system/references/ui-rules.md +14 -0
- package/skills/diagnosing-bugs/SKILL.md +138 -0
- package/skills/frontend-design/SKILL.md +36 -0
- package/skills/handoff/SKILL.md +16 -0
- package/skills/improve-codebase-architecture/SKILL.md +71 -0
- package/skills/linter-negative/SKILL.md +74 -0
- package/skills/no-slop-ui/SKILL.md +26 -0
- package/skills/prototype/SKILL.md +26 -0
- package/skills/registry.json +44087 -0
- package/skills/tastemaker/SKILL.md +8 -0
- package/skills/tastemaker/references/tastemaker.md +14 -0
- package/skills/tdd/SKILL.md +38 -0
- package/skills/visual-review/SKILL.md +103 -0
- package/skills/visual-review/references/review-gates.md +42 -0
- package/skills/visual-review/references/surface-archetypes.md +37 -0
- package/skills/wizard/SKILL.md +44 -0
- package/skills/writing-for-agents/SKILL.md +81 -0
- package/src/cognition/cognition-store.js +251 -0
- package/src/cognition/cognitive-state.js +126 -0
- package/src/cognition/hypothesis-manager.js +113 -0
- package/src/cognition/observation-engine.js +112 -0
- package/src/cognition/observation-provenance.js +268 -0
- package/src/context/active-context-boundary.js +68 -0
- package/src/context/assembly.js +494 -0
- package/src/context/assessment-rules.js +55 -0
- package/src/context/capsule-staleness.js +267 -0
- package/src/context/capsules.js +2 -0
- package/src/context/compare-array.js +98 -0
- package/src/context/compare-object.js +89 -0
- package/src/context/compare-values.js +42 -0
- package/src/context/context-assembly-contract.js +71 -0
- package/src/context/context-budget-manager.js +232 -0
- package/src/context/context-builder.js +49 -0
- package/src/context/context-cache.js +55 -0
- package/src/context/context-capture.js +243 -0
- package/src/context/context-classification.js +199 -0
- package/src/context/context-compaction.js +179 -0
- package/src/context/context-decision-audit.js +267 -0
- package/src/context/context-demotion.js +173 -0
- package/src/context/context-dictionary.js +106 -0
- package/src/context/context-drift-detection.js +225 -0
- package/src/context/context-evaluation.js +374 -0
- package/src/context/context-event-ingress.js +100 -0
- package/src/context/context-event-normalization.js +224 -0
- package/src/context/context-feedback-loop.js +216 -0
- package/src/context/context-freshness.js +21 -0
- package/src/context/context-graph-builder.js +71 -0
- package/src/context/context-graph.js +451 -0
- package/src/context/context-history-relevance.js +321 -0
- package/src/context/context-interception.js +145 -0
- package/src/context/context-invalidation-events.js +154 -0
- package/src/context/context-lifecycle.js +170 -0
- package/src/context/context-loop-prevention.js +347 -0
- package/src/context/context-manager.js +340 -0
- package/src/context/context-memory-layers.js +265 -0
- package/src/context/context-minimality.js +166 -0
- package/src/context/context-normalization.js +130 -0
- package/src/context/context-optimization-metrics.js +434 -0
- package/src/context/context-output-policy.js +135 -0
- package/src/context/context-pack-authority.js +157 -0
- package/src/context/context-promotion.js +172 -0
- package/src/context/context-query-contract.js +101 -0
- package/src/context/context-ranking.js +164 -0
- package/src/context/context-rate-distortion.js +261 -0
- package/src/context/context-recovery.js +209 -0
- package/src/context/context-retrieval-integration.js +60 -0
- package/src/context/context-retrieval.js +93 -0
- package/src/context/context-router.js +457 -0
- package/src/context/context-routing-evaluation.js +331 -0
- package/src/context/context-routing-observability.js +296 -0
- package/src/context/context-routing-shadow.js +237 -0
- package/src/context/context-scope.js +181 -0
- package/src/context/context-snapshot.js +226 -0
- package/src/context/context-source-registry.js +95 -0
- package/src/context/context-sufficiency-gate.js +120 -0
- package/src/context/context-sufficiency-oracle.js +167 -0
- package/src/context/context-telemetry.js +176 -0
- package/src/context/context.js +633 -0
- package/src/context/dependency-closure.js +246 -0
- package/src/context/page-fault-tracker.js +275 -0
- package/src/context/prompt-engine.js +74 -0
- package/src/context/runtime-context-graph.js +260 -0
- package/src/context/selection.js +2 -0
- package/src/context/session.js +2 -0
- package/src/context/sufficiency-contract.js +300 -0
- package/src/context/tokenizer.js +17 -0
- package/src/context-benchmark-router.mjs +125 -0
- package/src/evidence/evidence-engine.js +161 -0
- package/src/evidence/evidence-factory.js +3 -0
- package/src/evidence/evidence-freshness.js +463 -0
- package/src/evidence/evidence-gap.js +76 -0
- package/src/evidence/evidence-lineage.js +525 -0
- package/src/evidence/evidence.js +136 -0
- package/src/evidence/freshness-validation.js +187 -0
- package/src/evidence/lineage.js +4 -0
- package/src/execution/action-evaluation.js +179 -0
- package/src/execution/circuit-breaker.js +70 -0
- package/src/execution/claim-interception.js +63 -0
- package/src/execution/execution-engine.js +258 -0
- package/src/execution/execution-intent.js +69 -0
- package/src/execution/execution-state.js +89 -0
- package/src/execution/formatting.js +60 -0
- package/src/execution/l1-sanitize.js +16 -0
- package/src/execution/l2-resource-guards.js +19 -0
- package/src/execution/l3-invariant-checker.js +5 -0
- package/src/execution/l4-containment.js +6 -0
- package/src/execution/runtime-guards.js +106 -0
- package/src/execution/strategy-identity.js +162 -0
- package/src/execution/tool-interceptor.js +7 -0
- package/src/execution/tool-result-classifier.js +63 -0
- package/src/integration/canary-deploy.js +143 -0
- package/src/integration/collector.js +59 -0
- package/src/integration/error-recovery.js +6 -0
- package/src/integration/event-bus.js +66 -0
- package/src/integration/health-checker.js +57 -0
- package/src/integration/logger.js +39 -0
- package/src/integration/message-handler.js +611 -0
- package/src/integration/opencode-db.js +153 -0
- package/src/integration/orchestration.js +419 -0
- package/src/integration/part-provenance.js +65 -0
- package/src/integration/rate-limiter.js +55 -0
- package/src/integration/release-gate.js +336 -0
- package/src/integration/rollback-manager.js +164 -0
- package/src/integration/router-adapter.js +269 -0
- package/src/integration/telemetry.js +45 -0
- package/src/integration/update-checker.js +10 -0
- package/src/persistence/event-sourcing.js +75 -0
- package/src/persistence/memory.js +501 -0
- package/src/persistence/migration-runner.js +16 -0
- package/src/persistence/persistence-manager.js +39 -0
- package/src/persistence/transaction-log.js +157 -0
- package/src/policy/action-capabilities.js +116 -0
- package/src/policy/action-policy.js +4 -0
- package/src/policy/assessment-engine.js +104 -0
- package/src/policy/assumption-tracking.js +66 -0
- package/src/policy/budget-enforcer.js +7 -0
- package/src/policy/config-validator.js +102 -0
- package/src/policy/execution-assessment.js +142 -0
- package/src/policy/failure-classification.js +5 -0
- package/src/policy/feature-flags.js +65 -0
- package/src/policy/governance-enforcement.js +118 -0
- package/src/policy/locale.js +15 -0
- package/src/policy/multi-signer.js +151 -0
- package/src/policy/policy-state-machine.js +97 -0
- package/src/policy/ponytail-constraint.js +38 -0
- package/src/policy/project-inspector.js +93 -0
- package/src/policy/query-builder.js +14 -0
- package/src/policy/request-classifier.js +79 -0
- package/src/policy/risk-engine.js +266 -0
- package/src/policy/scope-enforcement.js +140 -0
- package/src/policy/secret-scanner.js +68 -0
- package/src/policy/skill-routing.js +104 -0
- package/src/policy/strategy-capabilities.js +211 -0
- package/src/policy/strategy.js +6 -0
- package/src/policy/uncertainty.js +117 -0
- package/src/shared/container.js +44 -0
- package/src/shared/decision-types.js +24 -0
- package/src/shared/simple-test.js +160 -0
- package/src/shared/wam-events.js +52 -0
- package/src/skills/engine.js +1705 -0
- package/src/skills/skill-registry.js +19 -0
- package/src/skills/skill-routing.js +340 -0
- package/src/state/lifecycle-manager.js +113 -0
- package/src/state/operational-state.js +198 -0
- package/src/state/projections.js +47 -0
- package/src/state/replay-engine.js +5 -0
- package/src/state/requirement-ownership.js +20 -0
- package/src/state/requirement-state.js +152 -0
- package/src/state/run-event-identity.js +172 -0
- package/src/state/state-engine.js +19 -0
- package/src/state/state-machine.js +119 -0
- package/src/state/state-schema.js +318 -0
- package/src/state/state-store.js +335 -0
- package/src/state/state-transition.js +84 -0
- package/src/state/task-dependencies.js +287 -0
- package/src/state/task-execution.js +217 -0
- package/src/state/task-lifecycle.js +59 -0
- package/src/state/task-runs.js +361 -0
- package/src/state/task-state-file.js +86 -0
- package/src/state/task-state.js +41 -0
- package/src/state/task-status.js +54 -0
- package/src/state/task-store.js +48 -0
- package/src/state/wam-state.js +148 -0
- package/src/verification/completion-gate-simple.js +8 -0
- package/src/verification/completion-gate.js +183 -0
- package/src/verification/contract.js +9 -0
- package/src/verification/contradiction-resolution.js +255 -0
- package/src/verification/diagnostic-engine.js +659 -0
- package/src/verification/false-completion-prevention.js +100 -0
- package/src/verification/hook-validator.js +6 -0
- package/src/verification/requirements.js +6 -0
- package/src/verification/verification-context.js +162 -0
- package/src/verification/verification-lifecycle.js +66 -0
- package/src/verification/verification-model.js +89 -0
- package/src/verification/verification-persistence.js +129 -0
- package/src/verification/verification-policy.js +127 -0
- package/src/verification/verification.js +415 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContextManager — Central orchestrator for context source registration,
|
|
3
|
+
* lifecycle, and memory-layer organization.
|
|
4
|
+
*
|
|
5
|
+
* Builds on top of ContextSourceRegistry and adds:
|
|
6
|
+
* - lifecycle-aware item management (CREATED/ACTIVE/COMPRESSED/...)
|
|
7
|
+
* - memory layer assignment (N0..N3 + ARCHIVE)
|
|
8
|
+
* - retrieval helpers filtered by layer/lifecycle
|
|
9
|
+
* - capacity-driven eviction with archive promotion
|
|
10
|
+
*
|
|
11
|
+
* The public API (register / getSource / listSources / getAllContext /
|
|
12
|
+
* size / clear) is preserved for backwards compatibility.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { ContextSourceRegistry } from "./context-source-registry.js";
|
|
16
|
+
import {
|
|
17
|
+
LIFECYCLE_STATES,
|
|
18
|
+
transition as lifecycleTransition,
|
|
19
|
+
isUsable as lifecycleIsUsable,
|
|
20
|
+
stalenessCheck,
|
|
21
|
+
} from "./context-lifecycle.js";
|
|
22
|
+
import {
|
|
23
|
+
MEMORY_LAYERS,
|
|
24
|
+
WORKING_LAYERS,
|
|
25
|
+
assignLayer as assignMemoryLayer,
|
|
26
|
+
sortByLayer,
|
|
27
|
+
groupByLayer,
|
|
28
|
+
selectEvictions,
|
|
29
|
+
archiveCandidates,
|
|
30
|
+
} from "./context-memory-layers.js";
|
|
31
|
+
import { promoteItem as _promoteItem } from "./context-promotion.js";
|
|
32
|
+
import { demoteItem as _demoteItem } from "./context-demotion.js";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {Object} ContextManagerOptions
|
|
36
|
+
* @property {ContextSourceRegistry} [registry] - Optional pre-built registry.
|
|
37
|
+
* @property {number} [workingCapacity] - Soft cap for total working items.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @typedef {Object} ManagedItem
|
|
42
|
+
* @property {string} id
|
|
43
|
+
* @property {string} type
|
|
44
|
+
* @property {string} [label]
|
|
45
|
+
* @property {string} [content]
|
|
46
|
+
* @property {string} [source]
|
|
47
|
+
* @property {number} [timestamp]
|
|
48
|
+
* @property {string} [scope]
|
|
49
|
+
* @property {string[]} [relatedRequirements]
|
|
50
|
+
* @property {Object} [metadata]
|
|
51
|
+
* @property {string} [lifecycle]
|
|
52
|
+
* @property {string} [memoryLayer]
|
|
53
|
+
* @property {number} [importance]
|
|
54
|
+
* @property {Array} [lifecycleHistory]
|
|
55
|
+
* @property {number} [updatedAt]
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
class ContextManager {
|
|
59
|
+
/**
|
|
60
|
+
* @param {ContextManagerOptions} [options]
|
|
61
|
+
*/
|
|
62
|
+
constructor(options = {}) {
|
|
63
|
+
this.registry = options.registry || new ContextSourceRegistry();
|
|
64
|
+
/** @type {number} */
|
|
65
|
+
this.workingCapacity = options.workingCapacity || 1024;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ---------------------------------------------------------------------
|
|
69
|
+
// Registration (preserved API)
|
|
70
|
+
// ---------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Register a context source via the underlying registry. The source is
|
|
74
|
+
* normalized with default lifecycle (CREATED) and memoryLayer (N3
|
|
75
|
+
* fallback). Returns the registered (and possibly updated) source.
|
|
76
|
+
*
|
|
77
|
+
* @param {ManagedItem} source
|
|
78
|
+
* @returns {ManagedItem}
|
|
79
|
+
*/
|
|
80
|
+
register(source) {
|
|
81
|
+
if (!source || typeof source !== "object" || !source.id) {
|
|
82
|
+
throw new TypeError("source must have an id");
|
|
83
|
+
}
|
|
84
|
+
const prepared = this._prepareItem(source);
|
|
85
|
+
return this.registry.register(prepared);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Remove a source by id.
|
|
90
|
+
* @param {string} id
|
|
91
|
+
* @returns {boolean}
|
|
92
|
+
*/
|
|
93
|
+
unregister(id) {
|
|
94
|
+
return this.registry.unregister(id);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Retrieve a single context source by id.
|
|
99
|
+
* @param {string} id
|
|
100
|
+
* @returns {ManagedItem | undefined}
|
|
101
|
+
*/
|
|
102
|
+
getSource(id) {
|
|
103
|
+
return this.registry.get(id);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* List all registered sources.
|
|
108
|
+
* @returns {ManagedItem[]}
|
|
109
|
+
*/
|
|
110
|
+
listSources() {
|
|
111
|
+
return this.registry.list();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* List sources filtered by type.
|
|
116
|
+
* @param {string} type
|
|
117
|
+
* @returns {ManagedItem[]}
|
|
118
|
+
*/
|
|
119
|
+
listSourcesByType(type) {
|
|
120
|
+
return this.registry.listByType(type);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Snapshot of all context sources keyed by id.
|
|
125
|
+
* @returns {Record<string, ManagedItem>}
|
|
126
|
+
*/
|
|
127
|
+
getAllContext() {
|
|
128
|
+
const result = {};
|
|
129
|
+
for (const source of this.registry.list()) {
|
|
130
|
+
result[source.id] = source;
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** @returns {number} */
|
|
136
|
+
get size() {
|
|
137
|
+
return this.registry.size;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Clear all registered sources. */
|
|
141
|
+
clear() {
|
|
142
|
+
this.registry.clear();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ---------------------------------------------------------------------
|
|
146
|
+
// Lifecycle operations
|
|
147
|
+
// ---------------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Apply a lifecycle transition to the item with the given id.
|
|
151
|
+
* @param {string} id
|
|
152
|
+
* @param {keyof typeof LIFECYCLE_STATES} next
|
|
153
|
+
* @param {Object} [meta]
|
|
154
|
+
* @returns {ManagedItem | undefined}
|
|
155
|
+
*/
|
|
156
|
+
transitionItem(id, next, meta = {}) {
|
|
157
|
+
const item = this.registry.get(id);
|
|
158
|
+
if (!item) return undefined;
|
|
159
|
+
const updated = lifecycleTransition(item, next, meta);
|
|
160
|
+
this.registry.register(updated);
|
|
161
|
+
return updated;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Return only items in CREATED or ACTIVE state.
|
|
166
|
+
* @returns {ManagedItem[]}
|
|
167
|
+
*/
|
|
168
|
+
listUsable() {
|
|
169
|
+
return this.registry.list().filter(lifecycleIsUsable);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Run a staleness sweep: transition any item whose age exceeds its
|
|
174
|
+
* state's threshold to STALE. Returns the list of items transitioned.
|
|
175
|
+
* @param {number} [now]
|
|
176
|
+
* @returns {ManagedItem[]}
|
|
177
|
+
*/
|
|
178
|
+
sweepStale(now = Date.now()) {
|
|
179
|
+
const transitioned = [];
|
|
180
|
+
for (const item of this.registry.list()) {
|
|
181
|
+
const next = stalenessCheck(item, now);
|
|
182
|
+
if (
|
|
183
|
+
next === LIFECYCLE_STATES.STALE &&
|
|
184
|
+
item.lifecycle !== LIFECYCLE_STATES.STALE
|
|
185
|
+
) {
|
|
186
|
+
const updated = lifecycleTransition(item, LIFECYCLE_STATES.STALE, {
|
|
187
|
+
reason: "stale-sweep",
|
|
188
|
+
});
|
|
189
|
+
this.registry.register(updated);
|
|
190
|
+
transitioned.push(updated);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return transitioned;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ---------------------------------------------------------------------
|
|
197
|
+
// Memory-layer operations
|
|
198
|
+
// ---------------------------------------------------------------------
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* List items belonging to a specific memory layer.
|
|
202
|
+
* @param {keyof typeof MEMORY_LAYERS} layer
|
|
203
|
+
* @returns {ManagedItem[]}
|
|
204
|
+
*/
|
|
205
|
+
listByLayer(layer) {
|
|
206
|
+
return this.registry.list().filter((it) => it.memoryLayer === layer);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* List working-memory items (N0..N3), ordered by layer priority.
|
|
211
|
+
* @returns {ManagedItem[]}
|
|
212
|
+
*/
|
|
213
|
+
listWorking() {
|
|
214
|
+
const items = this.registry.list().filter((it) =>
|
|
215
|
+
WORKING_LAYERS.includes(it.memoryLayer)
|
|
216
|
+
);
|
|
217
|
+
return sortByLayer(items);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Group all items by memory layer.
|
|
222
|
+
* @returns {Record<keyof typeof MEMORY_LAYERS, ManagedItem[]>}
|
|
223
|
+
*/
|
|
224
|
+
groupedByLayer() {
|
|
225
|
+
return groupByLayer(this.registry.list());
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Re-assign memory layer for a single item.
|
|
230
|
+
* @param {string} id
|
|
231
|
+
* @param {keyof typeof MEMORY_LAYERS} [layer]
|
|
232
|
+
* @returns {ManagedItem | undefined}
|
|
233
|
+
*/
|
|
234
|
+
setLayer(id, layer) {
|
|
235
|
+
const item = this.registry.get(id);
|
|
236
|
+
if (!item) return undefined;
|
|
237
|
+
const updated = assignMemoryLayer(item, layer);
|
|
238
|
+
this.registry.register(updated);
|
|
239
|
+
return updated;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Evict items to satisfy capacity constraints. Evicted items are
|
|
244
|
+
* transitioned to COMPRESSED and moved to the ARCHIVE layer so they
|
|
245
|
+
* remain queryable for provenance.
|
|
246
|
+
* @returns {{evicted: ManagedItem[], remaining: number}}
|
|
247
|
+
*/
|
|
248
|
+
evictForCapacity() {
|
|
249
|
+
const items = this.registry.list();
|
|
250
|
+
const evictions = selectEvictions(items);
|
|
251
|
+
for (const item of evictions) {
|
|
252
|
+
let updated = lifecycleTransition(item, LIFECYCLE_STATES.COMPRESSED, {
|
|
253
|
+
reason: "capacity-eviction",
|
|
254
|
+
});
|
|
255
|
+
updated = assignMemoryLayer(updated, MEMORY_LAYERS.ARCHIVE);
|
|
256
|
+
this.registry.register(updated);
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
evicted: evictions,
|
|
260
|
+
remaining: this.registry.size,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Move every COMPRESSED/STALE/INVALIDATED/ARCHIVED item into the
|
|
266
|
+
* ARCHIVE tier so retrieval helpers can treat archive as a single
|
|
267
|
+
* provenance surface.
|
|
268
|
+
* @returns {ManagedItem[]}
|
|
269
|
+
*/
|
|
270
|
+
promoteArchive() {
|
|
271
|
+
const items = this.registry.list();
|
|
272
|
+
const candidates = archiveCandidates(items);
|
|
273
|
+
const updated = [];
|
|
274
|
+
for (const item of candidates) {
|
|
275
|
+
if (item.memoryLayer === MEMORY_LAYERS.ARCHIVE) continue;
|
|
276
|
+
const next = assignMemoryLayer(item, MEMORY_LAYERS.ARCHIVE);
|
|
277
|
+
this.registry.register(next);
|
|
278
|
+
updated.push(next);
|
|
279
|
+
}
|
|
280
|
+
return updated;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ---------------------------------------------------------------------
|
|
284
|
+
// Promote / demote (integrated API)
|
|
285
|
+
// ---------------------------------------------------------------------
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Promote a registered item to a higher-priority memory layer.
|
|
289
|
+
* Delegates to context-promotion.js promoteItem, then persists
|
|
290
|
+
* the result back into the registry.
|
|
291
|
+
* @param {string} id
|
|
292
|
+
* @param {keyof typeof MEMORY_LAYERS} targetLayer (N0–N3)
|
|
293
|
+
* @returns {{item: ManagedItem, event: Object} | undefined}
|
|
294
|
+
*/
|
|
295
|
+
promoteItem(id, targetLayer) {
|
|
296
|
+
const item = this.registry.get(id);
|
|
297
|
+
if (!item) return undefined;
|
|
298
|
+
const result = _promoteItem(item, targetLayer);
|
|
299
|
+
this.registry.register(result.item);
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Demote a registered item to a lower-priority memory layer
|
|
305
|
+
* or ARCHIVE. Delegates to context-demotion.js demoteItem,
|
|
306
|
+
* then persists the result back into the registry.
|
|
307
|
+
* @param {string} id
|
|
308
|
+
* @param {keyof typeof MEMORY_LAYERS} targetLayer (N0–N3 or ARCHIVE)
|
|
309
|
+
* @returns {{item: ManagedItem, event: Object} | undefined}
|
|
310
|
+
*/
|
|
311
|
+
demoteItem(id, targetLayer) {
|
|
312
|
+
const item = this.registry.get(id);
|
|
313
|
+
if (!item) return undefined;
|
|
314
|
+
const result = _demoteItem(item, targetLayer);
|
|
315
|
+
this.registry.register(result.item);
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ---------------------------------------------------------------------
|
|
320
|
+
// Internal helpers
|
|
321
|
+
// ---------------------------------------------------------------------
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Apply defaults (lifecycle, memoryLayer, timestamp) to an incoming
|
|
325
|
+
* item before storage. Never mutates the caller's object.
|
|
326
|
+
* @param {ManagedItem} source
|
|
327
|
+
* @returns {ManagedItem}
|
|
328
|
+
*/
|
|
329
|
+
_prepareItem(source) {
|
|
330
|
+
const base = {
|
|
331
|
+
...source,
|
|
332
|
+
lifecycle: source.lifecycle || LIFECYCLE_STATES.CREATED,
|
|
333
|
+
timestamp: source.timestamp || Date.now(),
|
|
334
|
+
metadata: source.metadata || {},
|
|
335
|
+
};
|
|
336
|
+
return assignMemoryLayer(base, source.memoryLayer);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export { ContextManager };
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Memory Layers — hierarchical memory tiers for context items.
|
|
3
|
+
*
|
|
4
|
+
* Working Memory (active, in-session):
|
|
5
|
+
* N0 — Identity / goal
|
|
6
|
+
* N1 — Domain / project
|
|
7
|
+
* N2 — Current state / evidence
|
|
8
|
+
* N3 — Retrieval / exploration
|
|
9
|
+
*
|
|
10
|
+
* Archive (durable, off-session):
|
|
11
|
+
* ARCHIVE — historical, compressed, retained for provenance
|
|
12
|
+
*
|
|
13
|
+
* Each tier defines:
|
|
14
|
+
* - a priority (lower number = higher priority for assembly)
|
|
15
|
+
* - capacity hints (soft limits; actual eviction done by manager)
|
|
16
|
+
* - inclusion rules (which ContextCategory types belong here)
|
|
17
|
+
* - lifecycle expectations (which states are typical for this tier)
|
|
18
|
+
*
|
|
19
|
+
* Compatibility: tier assignment is read from item.memoryLayer, falling
|
|
20
|
+
* back to inference by category/importance so legacy items without the
|
|
21
|
+
* field still sort correctly.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { LIFECYCLE_STATES } from "./context-lifecycle.js";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {"N0"|"N1"|"N2"|"N3"|"ARCHIVE"} MemoryLayer
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/** @type {Record<MemoryLayer, MemoryLayer>} */
|
|
31
|
+
export const MEMORY_LAYERS = Object.freeze({
|
|
32
|
+
N0: "N0",
|
|
33
|
+
N1: "N1",
|
|
34
|
+
N2: "N2",
|
|
35
|
+
N3: "N3",
|
|
36
|
+
ARCHIVE: "ARCHIVE",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
/** @type {MemoryLayer[]} */
|
|
40
|
+
export const WORKING_LAYERS = Object.freeze(["N0", "N1", "N2", "N3"]);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Tier metadata — priority (lower = higher priority), capacity (max
|
|
44
|
+
* items suggested), and allowed categories.
|
|
45
|
+
* @type {Record<MemoryLayer, {priority: number, capacity: number, categories: string[], purpose: string}>}
|
|
46
|
+
*/
|
|
47
|
+
function freezeLayer(obj) {
|
|
48
|
+
for (const k of Object.keys(obj)) {
|
|
49
|
+
if (typeof obj[k] === "object" && obj[k] !== null) {
|
|
50
|
+
freezeLayer(obj[k]);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return Object.freeze(obj);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const LAYER_CONFIG = freezeLayer({
|
|
57
|
+
N0: {
|
|
58
|
+
priority: 0,
|
|
59
|
+
capacity: 16,
|
|
60
|
+
purpose: "Identity / goal — who the user/agent is and the headline objective.",
|
|
61
|
+
categories: ["REQUIREMENT", "DECISION"],
|
|
62
|
+
},
|
|
63
|
+
N1: {
|
|
64
|
+
priority: 1,
|
|
65
|
+
capacity: 64,
|
|
66
|
+
purpose: "Domain / project — stable project facts, conventions, scope.",
|
|
67
|
+
categories: ["FACT", "REQUIREMENT"],
|
|
68
|
+
},
|
|
69
|
+
N2: {
|
|
70
|
+
priority: 2,
|
|
71
|
+
capacity: 256,
|
|
72
|
+
purpose: "Current state / evidence — in-flight state, observations, evidence.",
|
|
73
|
+
categories: ["OBSERVATION", "EVIDENCE", "RESULT", "ERROR"],
|
|
74
|
+
},
|
|
75
|
+
N3: {
|
|
76
|
+
priority: 3,
|
|
77
|
+
capacity: 128,
|
|
78
|
+
purpose: "Retrieval / exploration — tentative claims, hypotheses, search traces.",
|
|
79
|
+
categories: ["CLAIM", "ASSUMPTION", "ACTION"],
|
|
80
|
+
},
|
|
81
|
+
ARCHIVE: {
|
|
82
|
+
priority: 4,
|
|
83
|
+
capacity: Infinity,
|
|
84
|
+
purpose: "Archive — compressed, historically retained for provenance.",
|
|
85
|
+
categories: ["FACT", "OBSERVATION", "CLAIM", "ASSUMPTION", "DECISION",
|
|
86
|
+
"REQUIREMENT", "EVIDENCE", "ERROR", "ACTION", "RESULT"],
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Default category → layer mapping when no explicit memoryLayer is set
|
|
92
|
+
* on the item. Categories listed in multiple layers resolve to the
|
|
93
|
+
* highest-priority layer (smallest priority number).
|
|
94
|
+
*/
|
|
95
|
+
const CATEGORY_DEFAULT_LAYER = Object.freeze({
|
|
96
|
+
REQUIREMENT: "N0",
|
|
97
|
+
DECISION: "N0",
|
|
98
|
+
FACT: "N1",
|
|
99
|
+
OBSERVATION: "N2",
|
|
100
|
+
EVIDENCE: "N2",
|
|
101
|
+
RESULT: "N2",
|
|
102
|
+
ERROR: "N2",
|
|
103
|
+
CLAIM: "N3",
|
|
104
|
+
ASSUMPTION: "N3",
|
|
105
|
+
ACTION: "N3",
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Validate that a value is a known memory layer.
|
|
110
|
+
* @param {unknown} layer
|
|
111
|
+
* @returns {boolean}
|
|
112
|
+
*/
|
|
113
|
+
export function isMemoryLayer(layer) {
|
|
114
|
+
return typeof layer === "string" && layer in MEMORY_LAYERS;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get the tier config for a layer.
|
|
119
|
+
* @param {MemoryLayer} layer
|
|
120
|
+
* @returns {typeof LAYER_CONFIG[MemoryLayer]}
|
|
121
|
+
*/
|
|
122
|
+
export function getLayerConfig(layer) {
|
|
123
|
+
if (!isMemoryLayer(layer)) {
|
|
124
|
+
throw new TypeError(`Unknown memory layer: ${String(layer)}`);
|
|
125
|
+
}
|
|
126
|
+
return LAYER_CONFIG[layer];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Assign a memory layer to an item. Priority of resolution:
|
|
131
|
+
* 1. Explicit item.memoryLayer if valid.
|
|
132
|
+
* 2. item.metadata.layer if valid.
|
|
133
|
+
* 3. Default by item.category (if provided) via CATEGORY_DEFAULT_LAYER.
|
|
134
|
+
* 4. N3 as the safest fallback (exploration tier absorbs unknowns).
|
|
135
|
+
*
|
|
136
|
+
* Returns a NEW item with `memoryLayer` populated; original is not mutated.
|
|
137
|
+
* @param {Object} item
|
|
138
|
+
* @param {MemoryLayer} [explicitLayer]
|
|
139
|
+
* @returns {Object}
|
|
140
|
+
*/
|
|
141
|
+
export function assignLayer(item, explicitLayer) {
|
|
142
|
+
if (!item || typeof item !== "object") {
|
|
143
|
+
throw new TypeError("item must be a non-null object");
|
|
144
|
+
}
|
|
145
|
+
let layer = explicitLayer;
|
|
146
|
+
if (!isMemoryLayer(layer) && isMemoryLayer(item.memoryLayer)) {
|
|
147
|
+
layer = item.memoryLayer;
|
|
148
|
+
}
|
|
149
|
+
if (!isMemoryLayer(layer) && item.metadata && isMemoryLayer(item.metadata.layer)) {
|
|
150
|
+
layer = item.metadata.layer;
|
|
151
|
+
}
|
|
152
|
+
if (!isMemoryLayer(layer) && item.category && CATEGORY_DEFAULT_LAYER[item.category]) {
|
|
153
|
+
layer = CATEGORY_DEFAULT_LAYER[item.category];
|
|
154
|
+
}
|
|
155
|
+
if (!isMemoryLayer(layer)) {
|
|
156
|
+
layer = MEMORY_LAYERS.N3;
|
|
157
|
+
}
|
|
158
|
+
return { ...item, memoryLayer: layer };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Sort items by tier priority (N0 first, ARCHIVE last). Stable for items
|
|
163
|
+
* within the same layer: original order preserved.
|
|
164
|
+
* @param {Object[]} items
|
|
165
|
+
* @returns {Object[]}
|
|
166
|
+
*/
|
|
167
|
+
export function sortByLayer(items) {
|
|
168
|
+
if (!Array.isArray(items)) {
|
|
169
|
+
throw new TypeError("items must be an array");
|
|
170
|
+
}
|
|
171
|
+
return items
|
|
172
|
+
.map((item, idx) => ({ item, idx }))
|
|
173
|
+
.sort((a, b) => {
|
|
174
|
+
const pa = LAYER_CONFIG[a.item.memoryLayer || "N3"].priority;
|
|
175
|
+
const pb = LAYER_CONFIG[b.item.memoryLayer || "N3"].priority;
|
|
176
|
+
if (pa !== pb) return pa - pb;
|
|
177
|
+
return a.idx - b.idx;
|
|
178
|
+
})
|
|
179
|
+
.map(({ item }) => item);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Group items by memory layer.
|
|
184
|
+
* @param {Object[]} items
|
|
185
|
+
* @returns {Record<MemoryLayer, Object[]>}
|
|
186
|
+
*/
|
|
187
|
+
export function groupByLayer(items) {
|
|
188
|
+
if (!Array.isArray(items)) {
|
|
189
|
+
throw new TypeError("items must be an array");
|
|
190
|
+
}
|
|
191
|
+
const out = { N0: [], N1: [], N2: [], N3: [], ARCHIVE: [] };
|
|
192
|
+
for (const item of items) {
|
|
193
|
+
const layer = isMemoryLayer(item.memoryLayer) ? item.memoryLayer : MEMORY_LAYERS.N3;
|
|
194
|
+
out[layer].push(item);
|
|
195
|
+
}
|
|
196
|
+
return out;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Return the set of items that should be evicted because their tier
|
|
201
|
+
* exceeds its configured capacity. Eviction policy:
|
|
202
|
+
* - ACTIVE/CREATED items are protected until tier is over capacity.
|
|
203
|
+
* - Lowest importance first (importance default 0.5).
|
|
204
|
+
* - Among equal importance, oldest first.
|
|
205
|
+
* @param {Object[]} items
|
|
206
|
+
* @returns {Object[]} Items recommended for eviction (already-compressed
|
|
207
|
+
* or archive-eligible items moved out of working set).
|
|
208
|
+
*/
|
|
209
|
+
export function selectEvictions(items) {
|
|
210
|
+
if (!Array.isArray(items)) {
|
|
211
|
+
throw new TypeError("items must be an array");
|
|
212
|
+
}
|
|
213
|
+
const grouped = groupByLayer(items);
|
|
214
|
+
const evictions = [];
|
|
215
|
+
for (const layer of WORKING_LAYERS) {
|
|
216
|
+
const bucket = grouped[layer];
|
|
217
|
+
const cap = LAYER_CONFIG[layer].capacity;
|
|
218
|
+
if (bucket.length <= cap) continue;
|
|
219
|
+
const surplus = bucket.length - cap;
|
|
220
|
+
const sorted = bucket.slice().sort((a, b) => {
|
|
221
|
+
const ia = typeof a.importance === "number" ? a.importance : 0.5;
|
|
222
|
+
const ib = typeof b.importance === "number" ? b.importance : 0.5;
|
|
223
|
+
if (ia !== ib) return ia - ib; // least important first
|
|
224
|
+
return (a.timestamp || 0) - (b.timestamp || 0); // oldest first
|
|
225
|
+
});
|
|
226
|
+
for (let i = 0; i < surplus; i++) {
|
|
227
|
+
evictions.push(sorted[i]);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return evictions;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Promote items to ARCHIVE based on lifecycle state. Items in COMPRESSED,
|
|
235
|
+
* STALE, INVALIDATED, or ARCHIVED move to the archive tier.
|
|
236
|
+
* @param {Object[]} items
|
|
237
|
+
* @returns {Object[]}
|
|
238
|
+
*/
|
|
239
|
+
export function archiveCandidates(items) {
|
|
240
|
+
if (!Array.isArray(items)) {
|
|
241
|
+
throw new TypeError("items must be an array");
|
|
242
|
+
}
|
|
243
|
+
return items.filter((it) => {
|
|
244
|
+
const state = it.lifecycle || LIFECYCLE_STATES.CREATED;
|
|
245
|
+
return (
|
|
246
|
+
state === LIFECYCLE_STATES.COMPRESSED ||
|
|
247
|
+
state === LIFECYCLE_STATES.STALE ||
|
|
248
|
+
state === LIFECYCLE_STATES.INVALIDATED ||
|
|
249
|
+
state === LIFECYCLE_STATES.ARCHIVED
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export default {
|
|
255
|
+
MEMORY_LAYERS,
|
|
256
|
+
WORKING_LAYERS,
|
|
257
|
+
LAYER_CONFIG,
|
|
258
|
+
isMemoryLayer,
|
|
259
|
+
getLayerConfig,
|
|
260
|
+
assignLayer,
|
|
261
|
+
sortByLayer,
|
|
262
|
+
groupByLayer,
|
|
263
|
+
selectEvictions,
|
|
264
|
+
archiveCandidates,
|
|
265
|
+
};
|