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.
Files changed (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +318 -2
  3. package/index.js +1836 -0
  4. package/package.json +70 -4
  5. package/skills/codebase-design/SKILL.md +114 -0
  6. package/skills/design-contract/SKILL.md +27 -0
  7. package/skills/design-critique/SKILL.md +19 -0
  8. package/skills/design-system/SKILL.md +17 -0
  9. package/skills/design-system/references/image-analysis.md +14 -0
  10. package/skills/design-system/references/tokens.md +32 -0
  11. package/skills/design-system/references/ui-rules.md +14 -0
  12. package/skills/diagnosing-bugs/SKILL.md +138 -0
  13. package/skills/frontend-design/SKILL.md +36 -0
  14. package/skills/handoff/SKILL.md +16 -0
  15. package/skills/improve-codebase-architecture/SKILL.md +71 -0
  16. package/skills/linter-negative/SKILL.md +74 -0
  17. package/skills/no-slop-ui/SKILL.md +26 -0
  18. package/skills/prototype/SKILL.md +26 -0
  19. package/skills/registry.json +44087 -0
  20. package/skills/tastemaker/SKILL.md +8 -0
  21. package/skills/tastemaker/references/tastemaker.md +14 -0
  22. package/skills/tdd/SKILL.md +38 -0
  23. package/skills/visual-review/SKILL.md +103 -0
  24. package/skills/visual-review/references/review-gates.md +42 -0
  25. package/skills/visual-review/references/surface-archetypes.md +37 -0
  26. package/skills/wizard/SKILL.md +44 -0
  27. package/skills/writing-for-agents/SKILL.md +81 -0
  28. package/src/cognition/cognition-store.js +251 -0
  29. package/src/cognition/cognitive-state.js +126 -0
  30. package/src/cognition/hypothesis-manager.js +113 -0
  31. package/src/cognition/observation-engine.js +112 -0
  32. package/src/cognition/observation-provenance.js +268 -0
  33. package/src/context/active-context-boundary.js +68 -0
  34. package/src/context/assembly.js +494 -0
  35. package/src/context/assessment-rules.js +55 -0
  36. package/src/context/capsule-staleness.js +267 -0
  37. package/src/context/capsules.js +2 -0
  38. package/src/context/compare-array.js +98 -0
  39. package/src/context/compare-object.js +89 -0
  40. package/src/context/compare-values.js +42 -0
  41. package/src/context/context-assembly-contract.js +71 -0
  42. package/src/context/context-budget-manager.js +232 -0
  43. package/src/context/context-builder.js +49 -0
  44. package/src/context/context-cache.js +55 -0
  45. package/src/context/context-capture.js +243 -0
  46. package/src/context/context-classification.js +199 -0
  47. package/src/context/context-compaction.js +179 -0
  48. package/src/context/context-decision-audit.js +267 -0
  49. package/src/context/context-demotion.js +173 -0
  50. package/src/context/context-dictionary.js +106 -0
  51. package/src/context/context-drift-detection.js +225 -0
  52. package/src/context/context-evaluation.js +374 -0
  53. package/src/context/context-event-ingress.js +100 -0
  54. package/src/context/context-event-normalization.js +224 -0
  55. package/src/context/context-feedback-loop.js +216 -0
  56. package/src/context/context-freshness.js +21 -0
  57. package/src/context/context-graph-builder.js +71 -0
  58. package/src/context/context-graph.js +451 -0
  59. package/src/context/context-history-relevance.js +321 -0
  60. package/src/context/context-interception.js +145 -0
  61. package/src/context/context-invalidation-events.js +154 -0
  62. package/src/context/context-lifecycle.js +170 -0
  63. package/src/context/context-loop-prevention.js +347 -0
  64. package/src/context/context-manager.js +340 -0
  65. package/src/context/context-memory-layers.js +265 -0
  66. package/src/context/context-minimality.js +166 -0
  67. package/src/context/context-normalization.js +130 -0
  68. package/src/context/context-optimization-metrics.js +434 -0
  69. package/src/context/context-output-policy.js +135 -0
  70. package/src/context/context-pack-authority.js +157 -0
  71. package/src/context/context-promotion.js +172 -0
  72. package/src/context/context-query-contract.js +101 -0
  73. package/src/context/context-ranking.js +164 -0
  74. package/src/context/context-rate-distortion.js +261 -0
  75. package/src/context/context-recovery.js +209 -0
  76. package/src/context/context-retrieval-integration.js +60 -0
  77. package/src/context/context-retrieval.js +93 -0
  78. package/src/context/context-router.js +457 -0
  79. package/src/context/context-routing-evaluation.js +331 -0
  80. package/src/context/context-routing-observability.js +296 -0
  81. package/src/context/context-routing-shadow.js +237 -0
  82. package/src/context/context-scope.js +181 -0
  83. package/src/context/context-snapshot.js +226 -0
  84. package/src/context/context-source-registry.js +95 -0
  85. package/src/context/context-sufficiency-gate.js +120 -0
  86. package/src/context/context-sufficiency-oracle.js +167 -0
  87. package/src/context/context-telemetry.js +176 -0
  88. package/src/context/context.js +633 -0
  89. package/src/context/dependency-closure.js +246 -0
  90. package/src/context/page-fault-tracker.js +275 -0
  91. package/src/context/prompt-engine.js +74 -0
  92. package/src/context/runtime-context-graph.js +260 -0
  93. package/src/context/selection.js +2 -0
  94. package/src/context/session.js +2 -0
  95. package/src/context/sufficiency-contract.js +300 -0
  96. package/src/context/tokenizer.js +17 -0
  97. package/src/context-benchmark-router.mjs +125 -0
  98. package/src/evidence/evidence-engine.js +161 -0
  99. package/src/evidence/evidence-factory.js +3 -0
  100. package/src/evidence/evidence-freshness.js +463 -0
  101. package/src/evidence/evidence-gap.js +76 -0
  102. package/src/evidence/evidence-lineage.js +525 -0
  103. package/src/evidence/evidence.js +136 -0
  104. package/src/evidence/freshness-validation.js +187 -0
  105. package/src/evidence/lineage.js +4 -0
  106. package/src/execution/action-evaluation.js +179 -0
  107. package/src/execution/circuit-breaker.js +70 -0
  108. package/src/execution/claim-interception.js +63 -0
  109. package/src/execution/execution-engine.js +258 -0
  110. package/src/execution/execution-intent.js +69 -0
  111. package/src/execution/execution-state.js +89 -0
  112. package/src/execution/formatting.js +60 -0
  113. package/src/execution/l1-sanitize.js +16 -0
  114. package/src/execution/l2-resource-guards.js +19 -0
  115. package/src/execution/l3-invariant-checker.js +5 -0
  116. package/src/execution/l4-containment.js +6 -0
  117. package/src/execution/runtime-guards.js +106 -0
  118. package/src/execution/strategy-identity.js +162 -0
  119. package/src/execution/tool-interceptor.js +7 -0
  120. package/src/execution/tool-result-classifier.js +63 -0
  121. package/src/integration/canary-deploy.js +143 -0
  122. package/src/integration/collector.js +59 -0
  123. package/src/integration/error-recovery.js +6 -0
  124. package/src/integration/event-bus.js +66 -0
  125. package/src/integration/health-checker.js +57 -0
  126. package/src/integration/logger.js +39 -0
  127. package/src/integration/message-handler.js +611 -0
  128. package/src/integration/opencode-db.js +153 -0
  129. package/src/integration/orchestration.js +419 -0
  130. package/src/integration/part-provenance.js +65 -0
  131. package/src/integration/rate-limiter.js +55 -0
  132. package/src/integration/release-gate.js +336 -0
  133. package/src/integration/rollback-manager.js +164 -0
  134. package/src/integration/router-adapter.js +269 -0
  135. package/src/integration/telemetry.js +45 -0
  136. package/src/integration/update-checker.js +10 -0
  137. package/src/persistence/event-sourcing.js +75 -0
  138. package/src/persistence/memory.js +501 -0
  139. package/src/persistence/migration-runner.js +16 -0
  140. package/src/persistence/persistence-manager.js +39 -0
  141. package/src/persistence/transaction-log.js +157 -0
  142. package/src/policy/action-capabilities.js +116 -0
  143. package/src/policy/action-policy.js +4 -0
  144. package/src/policy/assessment-engine.js +104 -0
  145. package/src/policy/assumption-tracking.js +66 -0
  146. package/src/policy/budget-enforcer.js +7 -0
  147. package/src/policy/config-validator.js +102 -0
  148. package/src/policy/execution-assessment.js +142 -0
  149. package/src/policy/failure-classification.js +5 -0
  150. package/src/policy/feature-flags.js +65 -0
  151. package/src/policy/governance-enforcement.js +118 -0
  152. package/src/policy/locale.js +15 -0
  153. package/src/policy/multi-signer.js +151 -0
  154. package/src/policy/policy-state-machine.js +97 -0
  155. package/src/policy/ponytail-constraint.js +38 -0
  156. package/src/policy/project-inspector.js +93 -0
  157. package/src/policy/query-builder.js +14 -0
  158. package/src/policy/request-classifier.js +79 -0
  159. package/src/policy/risk-engine.js +266 -0
  160. package/src/policy/scope-enforcement.js +140 -0
  161. package/src/policy/secret-scanner.js +68 -0
  162. package/src/policy/skill-routing.js +104 -0
  163. package/src/policy/strategy-capabilities.js +211 -0
  164. package/src/policy/strategy.js +6 -0
  165. package/src/policy/uncertainty.js +117 -0
  166. package/src/shared/container.js +44 -0
  167. package/src/shared/decision-types.js +24 -0
  168. package/src/shared/simple-test.js +160 -0
  169. package/src/shared/wam-events.js +52 -0
  170. package/src/skills/engine.js +1705 -0
  171. package/src/skills/skill-registry.js +19 -0
  172. package/src/skills/skill-routing.js +340 -0
  173. package/src/state/lifecycle-manager.js +113 -0
  174. package/src/state/operational-state.js +198 -0
  175. package/src/state/projections.js +47 -0
  176. package/src/state/replay-engine.js +5 -0
  177. package/src/state/requirement-ownership.js +20 -0
  178. package/src/state/requirement-state.js +152 -0
  179. package/src/state/run-event-identity.js +172 -0
  180. package/src/state/state-engine.js +19 -0
  181. package/src/state/state-machine.js +119 -0
  182. package/src/state/state-schema.js +318 -0
  183. package/src/state/state-store.js +335 -0
  184. package/src/state/state-transition.js +84 -0
  185. package/src/state/task-dependencies.js +287 -0
  186. package/src/state/task-execution.js +217 -0
  187. package/src/state/task-lifecycle.js +59 -0
  188. package/src/state/task-runs.js +361 -0
  189. package/src/state/task-state-file.js +86 -0
  190. package/src/state/task-state.js +41 -0
  191. package/src/state/task-status.js +54 -0
  192. package/src/state/task-store.js +48 -0
  193. package/src/state/wam-state.js +148 -0
  194. package/src/verification/completion-gate-simple.js +8 -0
  195. package/src/verification/completion-gate.js +183 -0
  196. package/src/verification/contract.js +9 -0
  197. package/src/verification/contradiction-resolution.js +255 -0
  198. package/src/verification/diagnostic-engine.js +659 -0
  199. package/src/verification/false-completion-prevention.js +100 -0
  200. package/src/verification/hook-validator.js +6 -0
  201. package/src/verification/requirements.js +6 -0
  202. package/src/verification/verification-context.js +162 -0
  203. package/src/verification/verification-lifecycle.js +66 -0
  204. package/src/verification/verification-model.js +89 -0
  205. package/src/verification/verification-persistence.js +129 -0
  206. package/src/verification/verification-policy.js +127 -0
  207. 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
+ };