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,261 @@
1
+ /**
2
+ * Context Rate-Distortion (C05 — context-rate-distortion).
3
+ *
4
+ * Combines Context Reduction Rate (CRR = tokensSaved / tokensFull) with
5
+ * Oracle Sufficiency verification (verifySufficiency) to compute the true
6
+ * rate-distortion curve:
7
+ *
8
+ * rate(b) = 1 - tokens(b) / tokensFull (compression ratio)
9
+ * distortion(b) = 1 - sufficient(b) (sufficiency loss)
10
+ *
11
+ * Where:
12
+ * tokensFull = sum of token counts of all candidates
13
+ * tokens(b) = sum of token counts of the selected set at budget b
14
+ * sufficient(b) = verifySufficiency(task, selected(b))
15
+ *
16
+ * The curve answers: "how aggressively can we compress while still being
17
+ * oracle-sufficient?"
18
+ *
19
+ * - RDReport.curve - [{rate, distortion, sufficient, budget, selected}]
20
+ * - RDReport.operatingPoint - first budget where sufficient===true (or null)
21
+ * - RDReport.maxRate - largest rate at which distortion===0
22
+ * - RDReport.crr - rate at the operating point (or max rate)
23
+ *
24
+ * Independence: uses oracle ground truth; does not consult scoring weights.
25
+ */
26
+
27
+ import {
28
+ computeRequiredClosure,
29
+ verifySufficiency,
30
+ } from "./context-sufficiency-oracle.js";
31
+
32
+ /**
33
+ * @typedef {Object} ContextItem
34
+ * @property {string} id
35
+ * @property {number} tokens
36
+ * @property {number} [priority] - higher = kept earlier under budget; ties broken by id
37
+ */
38
+
39
+ /**
40
+ * @typedef {Object} RDPoint
41
+ * @property {number} budget
42
+ * @property {number} rate - in [0, 1]
43
+ * @property {number} distortion - in [0, 1] (0 = sufficient)
44
+ * @property {boolean} sufficient
45
+ * @property {string[]} selected
46
+ * @property {number} tokensUsed
47
+ */
48
+
49
+ /**
50
+ * @typedef {Object} RDReport
51
+ * @property {string} taskId
52
+ * @property {number} fullTokens
53
+ * @property {RDPoint[]} curve
54
+ * @property {RDPoint|null} operatingPoint - first point with sufficient===true
55
+ * @property {number} crr - rate at operating point (or maxRate)
56
+ * @property {number} maxRate - largest rate where distortion===0
57
+ * @property {Object} stats
58
+ */
59
+
60
+ /**
61
+ * Greedy fill: pick items in priority-descending order until budget exhausted.
62
+ *
63
+ * @param {ContextItem[]} candidates
64
+ * @param {number} budget
65
+ * @returns {string[]} selected ids
66
+ */
67
+ function selectByBudget(candidates, budget) {
68
+ if (!Array.isArray(candidates) || budget <= 0) return [];
69
+ const sorted = candidates
70
+ .filter((c) => c && typeof c.id === "string" && typeof c.tokens === "number")
71
+ .slice()
72
+ .sort((a, b) => {
73
+ const pa = typeof a.priority === "number" ? a.priority : 0;
74
+ const pb = typeof b.priority === "number" ? b.priority : 0;
75
+ if (pb !== pa) return pb - pa;
76
+ return a.id.localeCompare(b.id);
77
+ });
78
+ const out = [];
79
+ let used = 0;
80
+ for (const c of sorted) {
81
+ if (used + c.tokens <= budget) {
82
+ out.push(c.id);
83
+ used += c.tokens;
84
+ }
85
+ }
86
+ return out;
87
+ }
88
+
89
+ /**
90
+ * Compute the rate-distortion curve.
91
+ *
92
+ * @param {Object} input
93
+ * @param {string} input.taskId
94
+ * @param {ContextItem[]} input.candidates
95
+ * @param {Object} input.oracleGraph
96
+ * @param {number[]} [input.budgets] - explicit budgets to sweep. If omitted,
97
+ * sweep is derived from candidates (each unique prefix budget).
98
+ * @returns {RDReport}
99
+ */
100
+ export function computeRateDistortion(input) {
101
+ if (!input || typeof input !== "object") {
102
+ throw new TypeError("computeRateDistortion: input required");
103
+ }
104
+ const { taskId, candidates, oracleGraph } = input;
105
+ if (typeof taskId !== "string" || taskId.length === 0) {
106
+ throw new TypeError("computeRateDistortion: taskId required");
107
+ }
108
+ if (!Array.isArray(candidates)) {
109
+ throw new TypeError("computeRateDistortion: candidates must be an array");
110
+ }
111
+ if (!oracleGraph || typeof oracleGraph.getNode !== "function") {
112
+ throw new TypeError("computeRateDistortion: oracleGraph required");
113
+ }
114
+
115
+ const requiredClosure = computeRequiredClosure(taskId, oracleGraph);
116
+ const requiredSet = new Set(requiredClosure);
117
+
118
+ const fullTokens = candidates.reduce(
119
+ (acc, c) => acc + (typeof c.tokens === "number" ? c.tokens : 0),
120
+ 0,
121
+ );
122
+
123
+ let budgets = input.budgets;
124
+ if (!Array.isArray(budgets) || budgets.length === 0) {
125
+ // derive: 0, plus cumulative token sums in priority order
126
+ const sorted = candidates
127
+ .filter((c) => c && typeof c.tokens === "number")
128
+ .slice()
129
+ .sort((a, b) => {
130
+ const pa = typeof a.priority === "number" ? a.priority : 0;
131
+ const pb = typeof b.priority === "number" ? b.priority : 0;
132
+ if (pb !== pa) return pb - pa;
133
+ return a.id.localeCompare(b.id);
134
+ });
135
+ const derived = [0];
136
+ let acc = 0;
137
+ for (const c of sorted) {
138
+ acc += c.tokens;
139
+ if (acc !== derived[derived.length - 1]) derived.push(acc);
140
+ }
141
+ if (derived[derived.length - 1] !== fullTokens) derived.push(fullTokens);
142
+ budgets = derived;
143
+ }
144
+ budgets = [...new Set(budgets.filter((b) => typeof b === "number" && b >= 0))].sort((a, b) => a - b);
145
+
146
+ /** @type {RDPoint[]} */
147
+ const curve = [];
148
+ let operatingPoint = null;
149
+ let maxRate = 0;
150
+
151
+ for (const budget of budgets) {
152
+ const selected = selectByBudget(candidates, budget);
153
+ const verdict = verifySufficiency(taskId, oracleGraph, selected);
154
+ const tokensUsed = selected.reduce((acc, id) => {
155
+ const c = candidates.find((x) => x.id === id);
156
+ return acc + (c && typeof c.tokens === "number" ? c.tokens : 0);
157
+ }, 0);
158
+ const rate = fullTokens === 0 ? 1 : 1 - tokensUsed / fullTokens;
159
+ const distortion = verdict.sufficient ? 0 : 1;
160
+ const point = {
161
+ budget,
162
+ rate: clamp01(rate),
163
+ distortion: clamp01(distortion),
164
+ sufficient: verdict.sufficient,
165
+ selected,
166
+ tokensUsed,
167
+ };
168
+ curve.push(point);
169
+ if (operatingPoint === null && verdict.sufficient) {
170
+ operatingPoint = point;
171
+ }
172
+ if (verdict.sufficient && point.rate > maxRate) {
173
+ maxRate = point.rate;
174
+ }
175
+ }
176
+
177
+ // Track which required nodes were ever included across any point
178
+ const everIncludedRequired = new Set();
179
+ for (const p of curve) {
180
+ for (const id of p.selected) if (requiredSet.has(id)) everIncludedRequired.add(id);
181
+ }
182
+ const unreachableRequired = requiredClosure.filter((id) => !everIncludedRequired.has(id));
183
+
184
+ const crr = operatingPoint ? operatingPoint.rate : (curve.length ? curve[curve.length - 1].rate : 0);
185
+
186
+ return {
187
+ taskId,
188
+ fullTokens,
189
+ curve,
190
+ operatingPoint,
191
+ crr: clamp01(crr),
192
+ maxRate: clamp01(maxRate),
193
+ stats: {
194
+ pointCount: curve.length,
195
+ requiredCount: requiredClosure.length,
196
+ unreachableRequiredCount: unreachableRequired.length,
197
+ unreachableRequired,
198
+ sufficientPoints: curve.filter((p) => p.sufficient).length,
199
+ },
200
+ };
201
+ }
202
+
203
+ /**
204
+ * Convenience: Context Reduction Rate at a specific budget.
205
+ *
206
+ * @param {RDReport} report
207
+ * @param {number} budget
208
+ * @returns {number} CRR in [0, 1]; 0 if budget not in curve.
209
+ */
210
+ export function crrAt(report, budget) {
211
+ if (!report || !Array.isArray(report.curve)) return 0;
212
+ const point = report.curve.find((p) => p.budget === budget);
213
+ if (!point) return 0;
214
+ return point.rate;
215
+ }
216
+
217
+ /**
218
+ * Convenience: distortion at a specific budget.
219
+ *
220
+ * @param {RDReport} report
221
+ * @param {number} budget
222
+ * @returns {number} distortion in [0, 1]; 1 if budget not in curve.
223
+ */
224
+ export function distortionAt(report, budget) {
225
+ if (!report || !Array.isArray(report.curve)) return 1;
226
+ const point = report.curve.find((p) => p.budget === budget);
227
+ if (!point) return 1;
228
+ return point.distortion;
229
+ }
230
+
231
+ /**
232
+ * Aggregate rate-distortion across multiple tasks into a mean curve.
233
+ *
234
+ * @param {RDReport[]} reports
235
+ * @returns {{meanCrr: number, meanMaxRate: number, pointCount: number}}
236
+ */
237
+ export function aggregateRateDistortion(reports) {
238
+ if (!Array.isArray(reports) || reports.length === 0) {
239
+ return { meanCrr: 0, meanMaxRate: 0, pointCount: 0 };
240
+ }
241
+ let crrSum = 0;
242
+ let maxSum = 0;
243
+ let pts = 0;
244
+ for (const r of reports) {
245
+ if (typeof r.crr === "number" && !Number.isNaN(r.crr)) crrSum += r.crr;
246
+ if (typeof r.maxRate === "number" && !Number.isNaN(r.maxRate)) maxSum += r.maxRate;
247
+ pts += r && Array.isArray(r.curve) ? r.curve.length : 0;
248
+ }
249
+ return {
250
+ meanCrr: crrSum / reports.length,
251
+ meanMaxRate: maxSum / reports.length,
252
+ pointCount: pts,
253
+ };
254
+ }
255
+
256
+ function clamp01(n) {
257
+ if (typeof n !== "number" || Number.isNaN(n)) return 0;
258
+ if (n < 0) return 0;
259
+ if (n > 1) return 1;
260
+ return n;
261
+ }
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Context Recovery — Reconstruct minimum corrective context when agent drifts.
3
+ *
4
+ * Change 78: reconstructMinimumCorrectiveContext()
5
+ *
6
+ * When an agent action has drifted from the goal, this module
7
+ * rebuilds the minimal context needed to get back on track.
8
+ *
9
+ * The recovery context includes:
10
+ * - The original goal/objective
11
+ * - What has already been accomplished (verified items)
12
+ * - The most recent relevant observations
13
+ * - Constraints that must be respected
14
+ * - Open questions that need resolution
15
+ */
16
+
17
+ /**
18
+ * @typedef {Object} RecoveryContext
19
+ * @property {string} goal - The original goal
20
+ * @property {Object[]} accomplished - Verified/accomplished items
21
+ * @property {Object[]} recentObservations - Most recent relevant observations
22
+ * @property {Object[]} constraints - Active constraints
23
+ * @property {Object[]} openQuestions - Questions needing resolution
24
+ * @property {string} driftReason - Why recovery was needed
25
+ * @property {number} timestamp - When recovery context was built
26
+ */
27
+
28
+ /**
29
+ * @typedef {Object} DriftReport
30
+ * @property {boolean} drifted - Whether drift was detected
31
+ * @property {string} reason - Description of the drift
32
+ * @property {number} confidence - 0-1 confidence of drift detection
33
+ */
34
+
35
+ /**
36
+ * Simple keyword check: does the content relate to the goal?
37
+ * @param {string} content
38
+ * @param {string} goal
39
+ * @returns {boolean}
40
+ */
41
+ function relatesToGoal(content, goal) {
42
+ if (!content || !goal) return false;
43
+ if (content === goal) return true;
44
+ const contentLower = String(content).toLowerCase();
45
+ const goalWords = String(goal).toLowerCase().split(/\s+/).filter((w) => w.length > 2);
46
+ if (goalWords.length === 0) return true;
47
+ const matches = goalWords.filter((w) => contentLower.includes(w));
48
+ return matches.length >= Math.ceil(goalWords.length / 2);
49
+ }
50
+
51
+ /**
52
+ * Change 78 — reconstructMinimumCorrectiveContext
53
+ *
54
+ * Builds a minimal context from available data when agent has drifted.
55
+ *
56
+ * @param {Object} params
57
+ * @param {string} params.goal - The original goal/objective
58
+ * @param {Object[]} [params.accomplished=[]] - Already completed/verified items
59
+ * @param {Object[]} [params.observations=[]] - Recent observations
60
+ * @param {Object[]} [params.constraints=[]] - Active constraints
61
+ * @param {Object[]} [params.openQuestions=[]] - Unresolved questions
62
+ * @param {Object[]} [params.recentActions=[]] - Recent actions taken
63
+ * @param {string} params.driftReason - Why recovery is needed
64
+ * @param {number} [params.maxObservations=5] - Max observations to include
65
+ * @param {number} [params.maxAccomplished=10] - Max accomplished items
66
+ * @returns {RecoveryContext}
67
+ */
68
+ export function reconstructMinimumCorrectiveContext({
69
+ goal,
70
+ accomplished = [],
71
+ observations = [],
72
+ constraints = [],
73
+ openQuestions = [],
74
+ recentActions = [],
75
+ driftReason,
76
+ maxObservations = 5,
77
+ maxAccomplished = 10,
78
+ }) {
79
+ if (!goal || typeof goal !== "string") {
80
+ throw new TypeError("goal must be a non-empty string");
81
+ }
82
+ if (!driftReason || typeof driftReason !== "string") {
83
+ throw new TypeError("driftReason must be a non-empty string");
84
+ }
85
+
86
+ // Accomplished: verified items, most recent first
87
+ const sortedAccomplished = [...accomplished]
88
+ .sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
89
+ .slice(0, maxAccomplished);
90
+
91
+ // Observations: filter for goal relevance, most recent first
92
+ const relevantObservations = observations
93
+ .filter((obs) => relatesToGoal(obs.content || "", goal))
94
+ .sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
95
+ .slice(0, maxObservations);
96
+
97
+ // If not enough goal-relevant observations, include recent actions as context
98
+ if (relevantObservations.length < 2 && recentActions.length > 0) {
99
+ const actionContext = recentActions
100
+ .sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
101
+ .slice(0, Math.max(1, maxObservations - relevantObservations.length));
102
+ // Blend: alternate between observations and actions
103
+ const blended = [];
104
+ const maxLen = Math.max(relevantObservations.length, actionContext.length);
105
+ for (let i = 0; i < maxLen; i++) {
106
+ if (relevantObservations[i]) blended.push(relevantObservations[i]);
107
+ if (actionContext[i]) blended.push(actionContext[i]);
108
+ }
109
+ return buildRecovery({
110
+ goal,
111
+ accomplished: sortedAccomplished,
112
+ observations: blended.slice(0, maxObservations),
113
+ constraints,
114
+ openQuestions,
115
+ driftReason,
116
+ });
117
+ }
118
+
119
+ return buildRecovery({
120
+ goal,
121
+ accomplished: sortedAccomplished,
122
+ observations: relevantObservations,
123
+ constraints,
124
+ openQuestions,
125
+ driftReason,
126
+ });
127
+ }
128
+
129
+ /**
130
+ * @param {Object} parts
131
+ * @returns {RecoveryContext}
132
+ */
133
+ function buildRecovery({ goal, accomplished, observations, constraints, openQuestions, driftReason }) {
134
+ return {
135
+ goal,
136
+ accomplished: accomplished || [],
137
+ recentObservations: observations || [],
138
+ constraints: constraints || [],
139
+ openQuestions: openQuestions || [],
140
+ driftReason,
141
+ timestamp: Date.now(),
142
+ };
143
+ }
144
+
145
+ /**
146
+ * Quick check: does the agent action represent a drift from the goal?
147
+ *
148
+ * @param {Object} agentAction - The action taken by the agent
149
+ * @param {string} goal - The original goal
150
+ * @returns {DriftReport}
151
+ */
152
+ export function detectActionDrift(agentAction, goal) {
153
+ if (!agentAction || typeof agentAction !== "object") {
154
+ return { drifted: false, reason: "No action provided", confidence: 0 };
155
+ }
156
+ if (!goal || typeof goal !== "string") {
157
+ return { drifted: false, reason: "No goal provided", confidence: 0 };
158
+ }
159
+
160
+ const actionContent = String(agentAction.content || agentAction.description || "").toLowerCase();
161
+ const actionType = String(agentAction.type || "").toLowerCase();
162
+
163
+ // Direct match: action type or content explicitly references goal
164
+ const directMatch = actionContent.includes(goal.toLowerCase()) ||
165
+ goal.toLowerCase().includes(actionContent) ||
166
+ actionContent === goal.toLowerCase();
167
+
168
+ if (directMatch) {
169
+ return { drifted: false, reason: "Action directly related to goal", confidence: 0.95 };
170
+ }
171
+
172
+ // Check for verb/topic overlap
173
+ const goalWords = goal.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
174
+ const contentWords = actionContent.split(/\s+/).filter((w) => w.length > 2);
175
+ const overlap = goalWords.filter((w) => contentWords.includes(w)).length;
176
+ const overlapRatio = goalWords.length > 0 ? overlap / goalWords.length : 0;
177
+
178
+ if (overlapRatio >= 0.3) {
179
+ return { drifted: false, reason: "Action has topic overlap with goal", confidence: 0.7 };
180
+ }
181
+
182
+ // Known drift indicators
183
+ const driftIndicators = [
184
+ "exploring", "wandering", "asking clarification", "need more info",
185
+ "unclear", "confused", "redundant", "waiting", "idle",
186
+ ];
187
+ const isDrift = driftIndicators.some((ind) => actionContent.includes(ind));
188
+
189
+ if (isDrift) {
190
+ return {
191
+ drifted: true,
192
+ reason: `Action contains drift indicator: "${actionContent.substring(0, 60)}"`,
193
+ confidence: 0.75,
194
+ signals: [actionContent],
195
+ };
196
+ }
197
+
198
+ // Low overlap, no drift indicators = likely drift but low confidence
199
+ if (overlapRatio < 0.1 && actionContent.length > 0) {
200
+ return {
201
+ drifted: true,
202
+ reason: `Low topic overlap (${Math.round(overlapRatio * 100)}%) between action and goal`,
203
+ confidence: 0.5,
204
+ signals: [`low_overlap:${Math.round(overlapRatio * 100)}%`],
205
+ };
206
+ }
207
+
208
+ return { drifted: false, reason: "Action appears goal-aligned", confidence: 0.8 };
209
+ }
@@ -0,0 +1,60 @@
1
+ import { retrieveContext } from "./context-retrieval.js";
2
+ import { retrieveActiveContext, isContextSufficient } from "./active-context-boundary.js";
3
+ import { buildContextQuery, matchesQuery, scoreRelevance, retrieveRelevantContext, CONTEXT_PURPOSE } from "./context-query-contract.js";
4
+ import { LIFECYCLE_STATES } from "./context-lifecycle.js";
5
+
6
+ /**
7
+ * High-level retrieval that integrates ContextManager with query contract and active boundary.
8
+ * @param {Object} manager - ContextManager instance (must have registry.list())
9
+ * @param {Object} query - Output from buildContextQuery() or compatible object
10
+ * @param {Object} options
11
+ * @param {number} [options.maxResults=50]
12
+ * @param {boolean} [options.includeHistory=false] - include INVALIDATED/ARCHIVED
13
+ * @returns {{relevantItems: ManagedItem[], sufficiencyCheck: Object, metadata: Object}}
14
+ */
15
+ export function integrateRetrieval(manager, query, options = {}) {
16
+ const { maxResults = 50, includeHistory = false } = options;
17
+ const startTime = Date.now();
18
+
19
+ if (!manager || typeof manager.registry?.list !== "function") {
20
+ throw new TypeError("integrateRetrieval requires a manager with registry.list()");
21
+ }
22
+
23
+ const allItems = manager.registry.list();
24
+
25
+ let relevantItems = retrieveRelevantContext(allItems, query);
26
+
27
+ if (!includeHistory) {
28
+ relevantItems = retrieveActiveContext(relevantItems, query.purpose);
29
+ }
30
+
31
+ if (maxResults > 0 && relevantItems.length > maxResults) {
32
+ relevantItems = relevantItems.slice(0, maxResults);
33
+ }
34
+
35
+ const sufficiencyCheck = isContextSufficient(relevantItems, query.purpose);
36
+
37
+ return {
38
+ relevantItems,
39
+ sufficiencyCheck,
40
+ metadata: {
41
+ totalItems: allItems.length,
42
+ retrievedCount: relevantItems.length,
43
+ purpose: query.purpose,
44
+ elapsedMs: Date.now() - startTime,
45
+ },
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Convenience: build query and retrieve in one call.
51
+ * @param {Object} manager - ContextManager instance
52
+ * @param {Object} params - buildContextQuery parameters
53
+ * @param {Object} options - integrateRetrieval options
54
+ */
55
+ export function retrieveForContext(manager, params, options = {}) {
56
+ const query = buildContextQuery(params);
57
+ return integrateRetrieval(manager, query, options);
58
+ }
59
+
60
+ export { CONTEXT_PURPOSE, buildContextQuery, matchesQuery, scoreRelevance, retrieveRelevantContext, retrieveActiveContext, isContextSufficient };
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Context Retrieval — retrieveContext(query, constraints).
3
+ *
4
+ * Change 69: Retrieves context items from a ContextManager/registry
5
+ * based on requirement relevance. Uses ranking from context-ranking.js
6
+ * and respects constraint filters (layer, lifecycle, admission).
7
+ *
8
+ * Invariant: Returns results ordered by relevance score (descending).
9
+ * Items that don't satisfy mandatory constraints are excluded.
10
+ */
11
+
12
+ import { rankContextItems } from "./context-ranking.js";
13
+
14
+ /**
15
+ * @typedef {Object} RetrievalConstraints
16
+ * @property {string[]} [layers] - Filter by memory layers (N0..N3, ARCHIVE).
17
+ * @property {string[]} [lifecycle] - Filter by lifecycle states.
18
+ * @property {string[]} [admission] - Filter by admission class.
19
+ * @property {number} [maxResults] - Max items to return.
20
+ * @property {number} [minScore] - Minimum relevance score threshold.
21
+ * @property {string[]} [requiredTerms] - Terms that must appear.
22
+ * @property {string[]} [excludedIds] - Item IDs to exclude.
23
+ */
24
+
25
+ /**
26
+ * @typedef {Object} RetrievalResult
27
+ * @property {Array} items - Ranked context items.
28
+ * @property {number} total - Total items before limiting.
29
+ * @property {number} returned - Number of items returned.
30
+ * @property {number} elapsedMs - Query execution time.
31
+ */
32
+
33
+ /**
34
+ * Retrieves and ranks context items based on query relevance and constraints.
35
+ *
36
+ * @param {string} query - Search query / requirement text.
37
+ * @param {RetrievalConstraints} [constraints={}] - Filter and limit options.
38
+ * @returns {RetrievalResult}
39
+ */
40
+ export function retrieveContext(query, constraints = {}) {
41
+ const startTime = Date.now();
42
+ const {
43
+ layers,
44
+ lifecycle,
45
+ admission,
46
+ maxResults = 50,
47
+ minScore = 0,
48
+ requiredTerms = [],
49
+ excludedIds = [],
50
+ } = constraints;
51
+
52
+ // Admission class filter (MANDATORY/CONDITIONAL/OPTIONAL)
53
+ const admissionFilter = (item) => {
54
+ if (!admission || !admission.length) return true;
55
+ const itemAdmission = item.admission || item.admissionClass || "OPTIONAL";
56
+ return admission.includes(itemAdmission);
57
+ };
58
+
59
+ // Retrieve from ContextManager if provided, or use registry directly.
60
+ // Note: `manager` is used for API compatibility (not a standard property).
61
+ const registry = constraints.registry || constraints.manager;
62
+ let items = [];
63
+
64
+ if (registry) {
65
+ const allItems = registry.list ? registry.list() : Object.values(registry);
66
+ items = allItems.filter((item) => {
67
+ if (layers && !layers.includes(item.memoryLayer)) return false;
68
+ if (lifecycle && !lifecycle.includes(item.lifecycle)) return false;
69
+ if (excludedIds.includes(item.id)) return false;
70
+ if (!admissionFilter(item)) return false;
71
+ return true;
72
+ });
73
+ }
74
+
75
+ // Rank by relevance to query
76
+ const ranked = rankContextItems(items, query, { requiredTerms });
77
+
78
+ // Apply min score filter
79
+ const filtered = ranked.filter((item) => {
80
+ const score = item.relevanceScore ?? 0;
81
+ return score >= minScore;
82
+ });
83
+
84
+ const total = filtered.length;
85
+ const result = filtered.slice(0, maxResults);
86
+
87
+ return {
88
+ items: result,
89
+ total,
90
+ returned: result.length,
91
+ elapsedMs: Date.now() - startTime,
92
+ };
93
+ }