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,374 @@
1
+ /**
2
+ * WAM Context Evaluation — Benchmark suite for comparing context strategies.
3
+ *
4
+ * Runs deterministic benchmarks comparing:
5
+ * 1. Full Context
6
+ * 2. Semantic Top-K
7
+ * 3. WAM Context Routing
8
+ *
9
+ * No OpenCode integration. Runs in isolation.
10
+ */
11
+
12
+ import { ContextGraph } from "./context-graph.js";
13
+ import { resolveContext } from "./context-router.js";
14
+
15
+ // -- Types --
16
+
17
+ /**
18
+ * @typedef {Object} ContextEvaluation
19
+ * @property {string} strategy
20
+ * @property {boolean} taskSuccess
21
+ * @property {boolean} verificationSuccess
22
+ * @property {number} contextTokens
23
+ * @property {number} contextRecall
24
+ * @property {number} contextPrecision
25
+ * @property {number} dependencyCoverage
26
+ * @property {number} evidenceCoverage
27
+ * @property {string[]} missingDependencies
28
+ */
29
+
30
+ /**
31
+ * @typedef {Object} BenchmarkScenario
32
+ * @property {string} name
33
+ * @property {string} description
34
+ * @property {(graph: ContextGraph) => { taskId: string, expected: { requiredNodes: string[], missingDeps: string[] } }} setup
35
+ */
36
+
37
+ // -- Token estimation --
38
+
39
+ function estimateTokens(text = "") {
40
+ return Math.ceil((text || "").length / 4);
41
+ }
42
+
43
+ function nodeTokens(node) {
44
+ return estimateTokens(node.content) + 20;
45
+ }
46
+
47
+ // -- Strategies --
48
+
49
+ /**
50
+ * Full Context: returns all nodes.
51
+ */
52
+ export function fullContext(graph, taskId) {
53
+ const nodes = graph.getNodes();
54
+ const edges = [];
55
+ for (const node of nodes) {
56
+ for (const edge of graph.getEdgesFrom(node.id)) {
57
+ edges.push(edge);
58
+ }
59
+ }
60
+ const tokenEstimate = nodes.reduce((sum, n) => sum + nodeTokens(n), 0);
61
+ return { nodes, edges, missing: [], complete: true, tokenEstimate };
62
+ }
63
+
64
+ /**
65
+ * Semantic Top-K: returns nodes with highest word overlap.
66
+ */
67
+ export function semanticTopK(graph, taskId, k = 10) {
68
+ const taskNode = graph.getNode(taskId);
69
+ if (!taskNode) return { nodes: [], edges: [], missing: [], complete: false, tokenEstimate: 0 };
70
+
71
+ const taskWords = new Set(
72
+ taskNode.content.toLowerCase().split(/\s+/).filter((w) => w.length > 3)
73
+ );
74
+
75
+ const scored = graph.getNodes().map((node) => {
76
+ const nodeWords = new Set(
77
+ node.content.toLowerCase().split(/\s+/).filter((w) => w.length > 3)
78
+ );
79
+ let overlap = 0;
80
+ for (const w of nodeWords) if (taskWords.has(w)) overlap++;
81
+ const score = nodeWords.size > 0 ? overlap / nodeWords.size : 0;
82
+ return { node, score };
83
+ });
84
+
85
+ scored.sort((a, b) => b.score - a.score);
86
+ const selected = scored.slice(0, k).map((s) => s.node);
87
+ const selectedIds = new Set(selected.map((n) => n.id));
88
+
89
+ const edges = [];
90
+ for (const node of selected) {
91
+ for (const edge of graph.getEdgesFrom(node.id)) {
92
+ if (selectedIds.has(edge.to)) edges.push(edge);
93
+ }
94
+ }
95
+
96
+ const tokenEstimate = selected.reduce((sum, n) => sum + nodeTokens(n), 0);
97
+ return { nodes: selected, edges, missing: [], complete: true, tokenEstimate };
98
+ }
99
+
100
+ // -- Evaluation metrics --
101
+
102
+ function computeMetrics(result, expected, graph) {
103
+ const resultIds = new Set(result.nodes.map((n) => n.id));
104
+ const expectedIds = new Set(expected.requiredNodes);
105
+
106
+ // Context Recall: how many expected nodes were retrieved
107
+ let recall = 0;
108
+ if (expectedIds.size > 0) {
109
+ for (const id of expectedIds) {
110
+ if (resultIds.has(id)) recall++;
111
+ }
112
+ recall /= expectedIds.size;
113
+ }
114
+
115
+ // Context Precision: how many retrieved nodes are actually needed
116
+ let precision = 0;
117
+ if (resultIds.size > 0) {
118
+ for (const id of resultIds) {
119
+ if (expectedIds.has(id)) precision++;
120
+ }
121
+ precision /= resultIds.size;
122
+ }
123
+
124
+ // Dependency Coverage: are all dependencies satisfied?
125
+ let depCoverage = 1;
126
+ if (expected.missingDeps.length > 0) {
127
+ const foundMissing = result.missing.filter((m) => m.type === "dependency");
128
+ depCoverage = foundMissing.length > 0 ? 0 : 1;
129
+ }
130
+
131
+ // Evidence Coverage: are evidence nodes present?
132
+ const evidenceNodes = graph.getNodes("evidence");
133
+ const requiredEvidence = evidenceNodes.filter((e) =>
134
+ [...expectedIds].some((id) => {
135
+ const edges = graph.getEdgesFrom(id);
136
+ return edges.some((edge) => edge.to === e.id);
137
+ })
138
+ );
139
+
140
+ let evidenceCoverage = 1;
141
+ if (requiredEvidence.length > 0) {
142
+ const foundEvidence = requiredEvidence.filter((e) => resultIds.has(e.id));
143
+ evidenceCoverage = foundEvidence.length / requiredEvidence.length;
144
+ }
145
+
146
+ // Task Success: all required nodes present
147
+ const taskSuccess = expectedIds.size > 0 &&
148
+ [...expectedIds].every((id) => resultIds.has(id));
149
+
150
+ // Verification Success: no contradictions and all evidence present
151
+ const contradictions = result.missing.filter((m) => m.type === "contradiction");
152
+ const verificationSuccess = contradictions.length === 0 && evidenceCoverage > 0.5;
153
+
154
+ return {
155
+ taskSuccess,
156
+ verificationSuccess,
157
+ contextTokens: result.tokenEstimate,
158
+ contextRecall: recall,
159
+ contextPrecision: precision,
160
+ dependencyCoverage: depCoverage,
161
+ evidenceCoverage,
162
+ missingDependencies: result.missing.filter((m) => m.type === "dependency").map((m) => m.description),
163
+ };
164
+ }
165
+
166
+ // -- Scenarios --
167
+
168
+ /** @type {BenchmarkScenario[]} */
169
+ export const SCENARIOS = [
170
+ {
171
+ name: "noise",
172
+ description: "100 nodes, 5 relevant, 95 irrelevant",
173
+ setup: (graph) => {
174
+ // Add 95 irrelevant nodes
175
+ for (let i = 0; i < 95; i++) {
176
+ graph.addNode({
177
+ id: `irr-${i}`,
178
+ type: "observation",
179
+ content: `Irrelevant observation ${i}: The weather is nice today and we had lunch`,
180
+ verified: false,
181
+ });
182
+ }
183
+
184
+ // Add 5 relevant nodes with dependencies
185
+ graph.addNode({ id: "task-1", type: "task", content: "Build authentication system", verified: false });
186
+ graph.addNode({ id: "output-1", type: "output", content: "JWT auth module with bcrypt", verified: true });
187
+ graph.addNode({ id: "evidence-1", type: "evidence", content: "Auth test passed with 100% coverage", verified: true });
188
+ graph.addNode({ id: "decision-1", type: "decision", content: "Use JWT tokens with 24h expiry", verified: false });
189
+ graph.addNode({ id: "constraint-1", type: "constraint", content: "Must use bcrypt for password hashing", verified: true });
190
+
191
+ graph.addEdge({ from: "task-1", to: "output-1", type: "produces" });
192
+ graph.addEdge({ from: "task-1", to: "decision-1", type: "produces" });
193
+ graph.addEdge({ from: "evidence-1", to: "output-1", type: "supports" });
194
+ graph.addEdge({ from: "task-1", to: "constraint-1", type: "requires_decision" });
195
+
196
+ return {
197
+ taskId: "task-1",
198
+ expected: {
199
+ requiredNodes: ["task-1", "output-1", "evidence-1", "decision-1", "constraint-1"],
200
+ missingDeps: [],
201
+ },
202
+ };
203
+ },
204
+ },
205
+
206
+ {
207
+ name: "hidden-dependency",
208
+ description: "Required context has low semantic similarity but is connected via graph",
209
+ setup: (graph) => {
210
+ graph.addNode({ id: "db-setup", type: "task", content: "Configure PostgreSQL connection pool", verified: false });
211
+ graph.addNode({ id: "db-output", type: "output", content: "PostgreSQL connection string and pool config", verified: true });
212
+ graph.addEdge({ from: "db-setup", to: "db-output", type: "produces" });
213
+
214
+ graph.addNode({ id: "api-task", type: "task", content: "Create REST endpoints for user management", verified: false });
215
+ graph.addNode({ id: "api-output", type: "output", content: "Express routes with authentication middleware", verified: true });
216
+ graph.addEdge({ from: "api-task", to: "api-output", type: "produces" });
217
+ graph.addEdge({ from: "api-task", to: "db-output", type: "requires_output" });
218
+
219
+ // This task has low semantic similarity to DB but depends on it transitively
220
+ graph.addNode({ id: "deploy-task", type: "task", content: "Deploy application to Kubernetes cluster", verified: false });
221
+ graph.addEdge({ from: "deploy-task", to: "api-output", type: "requires_output" });
222
+
223
+ return {
224
+ taskId: "deploy-task",
225
+ expected: {
226
+ requiredNodes: ["deploy-task", "api-output", "db-output"],
227
+ missingDeps: [],
228
+ },
229
+ };
230
+ },
231
+ },
232
+
233
+ {
234
+ name: "contradictory-evidence",
235
+ description: "Two pieces of evidence disagree",
236
+ setup: (graph) => {
237
+ graph.addNode({ id: "test-task", type: "task", content: "Verify feature works", verified: false });
238
+ graph.addNode({ id: "feature-output", type: "output", content: "Feature implementation", verified: true });
239
+ graph.addNode({ id: "evidence-pass", type: "evidence", content: "Unit test passed locally", verified: false });
240
+ graph.addNode({ id: "evidence-fail", type: "evidence", content: "Integration test failed on CI", verified: true });
241
+
242
+ graph.addEdge({ from: "test-task", to: "feature-output", type: "requires_output" });
243
+ graph.addEdge({ from: "test-task", to: "evidence-pass", type: "requires_evidence" });
244
+ graph.addEdge({ from: "test-task", to: "evidence-fail", type: "requires_evidence" });
245
+ graph.addEdge({ from: "evidence-pass", to: "evidence-fail", type: "contradicts" });
246
+
247
+ return {
248
+ taskId: "test-task",
249
+ expected: {
250
+ requiredNodes: ["test-task", "feature-output", "evidence-pass", "evidence-fail"],
251
+ missingDeps: [],
252
+ },
253
+ };
254
+ },
255
+ },
256
+
257
+ {
258
+ name: "stale-evidence",
259
+ description: "A previous decision is invalidated by newer evidence",
260
+ setup: (graph) => {
261
+ graph.addNode({ id: "old-decision", type: "decision", content: "Use MongoDB for data storage", verified: false });
262
+ graph.addNode({ id: "new-evidence", type: "evidence", content: "MongoDB performance degrades at scale, PostgreSQL recommended", verified: true });
263
+ graph.addNode({ id: "current-task", type: "task", content: "Design data layer", verified: false });
264
+
265
+ graph.addEdge({ from: "new-evidence", to: "old-decision", type: "invalidates" });
266
+ graph.addEdge({ from: "current-task", to: "old-decision", type: "requires_decision" });
267
+
268
+ return {
269
+ taskId: "current-task",
270
+ expected: {
271
+ requiredNodes: ["current-task", "old-decision", "new-evidence"],
272
+ missingDeps: [],
273
+ },
274
+ };
275
+ },
276
+ },
277
+
278
+ {
279
+ name: "missing-dependency",
280
+ description: "A task requires an output that does not exist",
281
+ setup: (graph) => {
282
+ graph.addNode({ id: "task-1", type: "task", content: "Build feature that depends on missing service", verified: false });
283
+ // Note: output-1 is NOT added to the graph
284
+ // Add edge manually to bypass validation (we're testing missing deps)
285
+ const edges = graph.edgesFrom.get("task-1") || [];
286
+ edges.push({ from: "task-1", to: "output-1", type: "requires_output", weight: 1.0 });
287
+ graph.edgesFrom.set("task-1", edges);
288
+
289
+ const toEdges = graph.edgesTo.get("output-1") || [];
290
+ toEdges.push({ from: "task-1", to: "output-1", type: "requires_output", weight: 1.0 });
291
+ graph.edgesTo.set("output-1", toEdges);
292
+
293
+ return {
294
+ taskId: "task-1",
295
+ expected: {
296
+ requiredNodes: ["task-1"],
297
+ missingDeps: ["output-1"],
298
+ },
299
+ };
300
+ },
301
+ },
302
+
303
+ {
304
+ name: "playwright-chromium",
305
+ description: "Browser automation with missing Chromium runtime",
306
+ setup: (graph) => {
307
+ graph.addNode({ id: "scrape-task", type: "task", content: "Scrape Lider website with Playwright", verified: false });
308
+ graph.addNode({ id: "browser-output", type: "output", content: "Scraped product data from Lider", verified: false });
309
+ graph.addNode({ id: "chromium-dep", type: "artifact", content: "Chromium browser binary installed", verified: false });
310
+ graph.addNode({ id: "playwright-dep", type: "artifact", content: "Playwright npm package installed", verified: true });
311
+
312
+ graph.addEdge({ from: "scrape-task", to: "browser-output", type: "produces" });
313
+ graph.addEdge({ from: "scrape-task", to: "chromium-dep", type: "depends_on_artifact" });
314
+ graph.addEdge({ from: "scrape-task", to: "playwright-dep", type: "depends_on_artifact" });
315
+
316
+ return {
317
+ taskId: "scrape-task",
318
+ expected: {
319
+ requiredNodes: ["scrape-task", "browser-output", "chromium-dep", "playwright-dep"],
320
+ missingDeps: [],
321
+ },
322
+ };
323
+ },
324
+ },
325
+ ];
326
+
327
+ // -- Benchmark runner --
328
+
329
+ /**
330
+ * Run a single scenario against all strategies.
331
+ * @param {BenchmarkScenario} scenario
332
+ * @returns {{ scenario: string, results: ContextEvaluation[] }}
333
+ */
334
+ export function runScenario(scenario) {
335
+ const graph = new ContextGraph();
336
+ const { taskId, expected } = scenario.setup(graph);
337
+
338
+ const strategies = [
339
+ { name: "full-context", fn: fullContext },
340
+ { name: "semantic-topk", fn: (g, id) => semanticTopK(g, id, 10) },
341
+ { name: "wam-routing", fn: resolveContext },
342
+ ];
343
+
344
+ const results = strategies.map(({ name, fn }) => {
345
+ const result = fn(graph, taskId);
346
+ const metrics = computeMetrics(result, expected, graph);
347
+ return { strategy: name, ...metrics };
348
+ });
349
+
350
+ return { scenario: scenario.name, results };
351
+ }
352
+
353
+ /**
354
+ * Run all scenarios.
355
+ * @returns {{ scenarios: { scenario: string, results: ContextEvaluation[] }[], summary: Record<string, number> }}
356
+ */
357
+ export function runAllScenarios() {
358
+ const scenarios = SCENARIOS.map(runScenario);
359
+
360
+ const summary = {};
361
+ for (const { results } of scenarios) {
362
+ for (const result of results) {
363
+ const key = `${result.strategy}`;
364
+ if (!summary[key]) {
365
+ summary[key] = { totalTokens: 0, successes: 0, total: 0 };
366
+ }
367
+ summary[key].totalTokens += result.contextTokens;
368
+ summary[key].total++;
369
+ if (result.taskSuccess) summary[key].successes++;
370
+ }
371
+ }
372
+
373
+ return { scenarios, summary };
374
+ }
@@ -0,0 +1,100 @@
1
+ export const CONTEXT_EVENT_TYPES = Object.freeze({
2
+ TASK_CREATED: "TASK_CREATED",
3
+ TASK_UPDATED: "TASK_UPDATED",
4
+ REQUIREMENT_CREATED: "REQUIREMENT_CREATED",
5
+ REQUIREMENT_UPDATED: "REQUIREMENT_UPDATED",
6
+ HYPOTHESIS_PROPOSED: "HYPOTHESIS_PROPOSED",
7
+ HYPOTHESIS_ASSESSED: "HYPOTHESIS_ASSESSED",
8
+ EXPERIMENT_STARTED: "EXPERIMENT_STARTED",
9
+ EXPERIMENT_COMPLETED: "EXPERIMENT_COMPLETED",
10
+ TOOL_STARTED: "TOOL_STARTED",
11
+ TOOL_FINISHED: "TOOL_FINISHED",
12
+ OBSERVATION_RECORDED: "OBSERVATION_RECORDED",
13
+ ASSESSMENT_COMPLETED: "ASSESSMENT_COMPLETED",
14
+ EVIDENCE_CREATED: "EVIDENCE_CREATED",
15
+ VERIFICATION_COMPLETED: "VERIFICATION_COMPLETED",
16
+ FILE_CHANGED: "FILE_CHANGED",
17
+ DECISION_RECORDED: "DECISION_RECORDED",
18
+ CONTEXT_INVALIDATED: "CONTEXT_INVALIDATED",
19
+ });
20
+
21
+ const VALID_CONTEXT_EVENT_TYPES = new Set(Object.values(CONTEXT_EVENT_TYPES));
22
+
23
+ let _contextEventBus = [];
24
+
25
+ export function resetContextEventBus() {
26
+ _contextEventBus = [];
27
+ }
28
+
29
+ export function ingestContextEvent(event) {
30
+ if (!event || typeof event !== "object") {
31
+ throw new Error("ingestContextEvent requires an event object");
32
+ }
33
+ if (!event.type || !VALID_CONTEXT_EVENT_TYPES.has(event.type)) {
34
+ throw new Error(`Invalid event type: ${event.type}`);
35
+ }
36
+ if (!event.taskId) {
37
+ throw new Error("event.taskId is required");
38
+ }
39
+ if (!event.timestamp) {
40
+ event.timestamp = Date.now();
41
+ }
42
+ const enriched = {
43
+ id: `evt-${event.timestamp}-${Math.random().toString(36).slice(2, 8)}`,
44
+ sessionId: event.sessionId || null,
45
+ executionId: event.executionId || null,
46
+ payload: event.payload || null,
47
+ provenance: event.provenance || null,
48
+ ...event,
49
+ };
50
+ _contextEventBus.push(enriched);
51
+ return enriched;
52
+ }
53
+
54
+ export function getContextEvents(filter = {}) {
55
+ return _contextEventBus.filter((event) => {
56
+ if (filter.taskId && event.taskId !== filter.taskId) return false;
57
+ if (filter.type && event.type !== filter.type) return false;
58
+ if (filter.since && event.timestamp < filter.since) return false;
59
+ if (filter.until && event.timestamp > filter.until) return false;
60
+ return true;
61
+ });
62
+ }
63
+
64
+ export function getLastContextEvent(taskId, type) {
65
+ const events = getContextEvents({ taskId, type });
66
+ return events.length > 0 ? events[events.length - 1] : null;
67
+ }
68
+
69
+ export function emitToolStarted({ taskId, callID, tool, args, sessionId }) {
70
+ return ingestContextEvent({
71
+ type: CONTEXT_EVENT_TYPES.TOOL_STARTED,
72
+ taskId,
73
+ sessionId,
74
+ executionId: callID,
75
+ payload: { tool, args },
76
+ });
77
+ }
78
+
79
+ export function emitToolFinished({ taskId, callID, tool, result, sessionId }) {
80
+ return ingestContextEvent({
81
+ type: CONTEXT_EVENT_TYPES.TOOL_FINISHED,
82
+ taskId,
83
+ sessionId,
84
+ executionId: callID,
85
+ payload: { tool, result },
86
+ });
87
+ }
88
+
89
+ export function emitObservation({ taskId, experimentId, assessment, executionId }) {
90
+ return ingestContextEvent({
91
+ type: CONTEXT_EVENT_TYPES.OBSERVATION_RECORDED,
92
+ taskId,
93
+ executionId,
94
+ payload: { experimentId, assessment },
95
+ });
96
+ }
97
+
98
+ export function getContextEventBus() {
99
+ return _contextEventBus.slice();
100
+ }
@@ -0,0 +1,224 @@
1
+ import { CONTEXT_EVENT_TYPES } from "./context-event-ingress.js";
2
+
3
+ export function normalizeContextItem(event) {
4
+ const type = event.type || "";
5
+ const payload = event.payload || {};
6
+
7
+ return {
8
+ id: event.id || `evt-${event.timestamp || Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
9
+ eventType: type,
10
+ taskId: event.taskId,
11
+ sessionId: event.sessionId || null,
12
+ executionId: event.executionId || null,
13
+ timestamp: event.timestamp || Date.now(),
14
+ content: normalizeContentByType(type, payload),
15
+ source: event.provenance || null,
16
+ lifecycle: inferLifecycle(type, payload),
17
+ classification: inferClassification(type, payload),
18
+ scope: inferScope(type, payload),
19
+ importance: inferImportance(type, payload),
20
+
21
+ requirementId: extractSingleId(payload.requirementId, payload.requirements),
22
+ hypothesisId: extractSingleId(payload.hypothesisId, payload.hypotheses),
23
+ experimentId: extractSingleId(payload.experimentId, payload.experiments),
24
+ observationId: extractObservationId(payload),
25
+ evidenceId: extractEvidenceId(payload),
26
+
27
+ requirementIds: extractRequirementIds(payload),
28
+ hypothesisIds: extractHypothesisIds(payload),
29
+ experimentIds: extractExperimentIds(payload),
30
+
31
+ mandatoryIncluded: inferMandatoryIncluded(type),
32
+ unresolvedCriticalUnknowns: inferUnresolvedCriticalUnknowns(type, payload),
33
+ };
34
+ }
35
+
36
+ function extractSingleId(single, array) {
37
+ if (single) return single;
38
+ if (Array.isArray(array) && array.length > 0) return array[0];
39
+ return null;
40
+ }
41
+
42
+ function extractObservationId(payload) {
43
+ if (payload?.observationId) return payload.observationId;
44
+ if (payload?.observation && payload?.observation?.id) return payload.observation.id;
45
+ // In some events, observation might be in the content after normalization, but we keep it simple.
46
+ return null;
47
+ }
48
+
49
+ function extractEvidenceId(payload) {
50
+ if (payload?.evidenceId) return payload.evidenceId;
51
+ if (payload?.evidence && payload?.evidence?.id) return payload.evidence.id;
52
+ return null;
53
+ }
54
+
55
+ function normalizeContentByType(eventType, payload) {
56
+ const op = eventType.toLowerCase();
57
+
58
+ if (op.includes("tool_started") || op.includes("tool_finished")) {
59
+ return {
60
+ tool: payload.tool || null,
61
+ args: payload.args || null,
62
+ result: payload.result || null,
63
+ error: payload.error || null,
64
+ };
65
+ }
66
+
67
+ if (op.includes("observation_recorded") || op.includes("assessment_completed")) {
68
+ return {
69
+ experimentId: payload.experimentId || null,
70
+ status: payload.status || null,
71
+ outcome: payload.outcome || null,
72
+ evidence: payload.evidence || null,
73
+ assessment: payload.assessment || null,
74
+ };
75
+ }
76
+
77
+ if (op.includes("requirement_created") || op.includes("requirement_updated")) {
78
+ return {
79
+ requirementId: payload.requirementId || null,
80
+ title: payload.title || null,
81
+ status: payload.status || null,
82
+ verified: payload.verified || null,
83
+ };
84
+ }
85
+
86
+ if (op.includes("hypothesis_proposed") || op.includes("hypothesis_assessed")) {
87
+ return {
88
+ hypothesisId: payload.hypothesisId || null,
89
+ statement: payload.statement || null,
90
+ status: payload.status || null,
91
+ confidence: payload.confidence || null,
92
+ };
93
+ }
94
+
95
+ if (op.includes("evidence_created")) {
96
+ return {
97
+ evidenceId: payload.evidenceId || null,
98
+ type: payload.type || null,
99
+ content: payload.content || null,
100
+ linkedTo: payload.linkedTo || null,
101
+ };
102
+ }
103
+
104
+ if (op.includes("verification_completed")) {
105
+ return {
106
+ requirementId: payload.requirementId || null,
107
+ verified: payload.verified || null,
108
+ level: payload.level || null,
109
+ };
110
+ }
111
+
112
+ if (op.includes("context_invalidated")) {
113
+ return {
114
+ reason: payload.reason || null,
115
+ invalidatedIds: payload.invalidatedIds || null,
116
+ scope: payload.scope || null,
117
+ };
118
+ }
119
+
120
+ if (op.includes("file_changed")) {
121
+ return {
122
+ path: payload.path || null,
123
+ changeType: payload.changeType || null,
124
+ };
125
+ }
126
+
127
+ if (op.includes("decision_recorded")) {
128
+ return {
129
+ decisionId: payload.decisionId || null,
130
+ content: payload.content || null,
131
+ basis: payload.basis || null,
132
+ };
133
+ }
134
+
135
+ return { raw: JSON.stringify(payload) };
136
+ }
137
+
138
+ function inferLifecycle(eventType) {
139
+ const normalized = eventType.toLowerCase();
140
+ if (normalized.includes("created")) return "CREATED";
141
+ if (normalized.includes("updated")) return "UPDATED";
142
+ if (normalized.includes("started")) return "STARTED";
143
+ if (normalized.includes("finished")) return "FINISHED";
144
+ if (normalized.includes("completed")) return "COMPLETED";
145
+ if (normalized.includes("invalidated")) return "INVALIDATED";
146
+ if (normalized.includes("archived")) return "ARCHIVED";
147
+ return "ACTIVE";
148
+ }
149
+
150
+ function inferClassification(eventType) {
151
+ const normalized = eventType.toLowerCase();
152
+ if (normalized.includes("decision")) return "DECISION";
153
+ if (normalized.includes("assessment")) return "ASSESSMENT";
154
+ if (normalized.includes("verified")) return "VERIFIED";
155
+ if (normalized.includes("invalidate")) return "INVALIDATION";
156
+ if (normalized.includes("recovery")) return "RECOVERY";
157
+ if (normalized.includes("tool")) return "ACTION";
158
+ if (normalized.includes("hypothesis")) return "HYPOTHESIS";
159
+ if (normalized.includes("requirement")) return "REQUIREMENT";
160
+ if (normalized.includes("context")) return "CONTEXT";
161
+ return "UNCLASSIFIED";
162
+ }
163
+
164
+ function inferScope(eventType) {
165
+ const normalized = eventType.toLowerCase();
166
+ if (normalized.includes("session")) return "SESSION";
167
+ if (normalized.includes("task")) return "TASK";
168
+ if (normalized.includes("task")) return "TASK";
169
+ if (normalized.includes("project")) return "PROJECT";
170
+ if (normalized.includes("global")) return "GLOBAL";
171
+ return "LOCAL";
172
+ }
173
+
174
+ function inferImportance(eventType) {
175
+ const normalized = eventType.toLowerCase();
176
+ if (normalized.includes("decision") || normalized.includes("critical")) return "HIGH";
177
+ if (normalized.includes("verification") || normalized.includes("completed")) return "MEDIUM";
178
+ if (normalized.includes("tool") || normalized.includes("action")) return "MEDIUM";
179
+ if (normalized.includes("hypothesis") || normalized.includes("hypo")) return "LOW";
180
+ if (normalized.includes("requirement")) return "LOW";
181
+ return "LOW";
182
+ }
183
+
184
+ function extractRequirementIds(payload) {
185
+ const ids = [];
186
+ if (payload?.requirementId) ids.push(payload.requirementId);
187
+ if (payload?.requirements && Array.isArray(payload.requirements)) {
188
+ payload.requirements.forEach((r) => { if (r && r.id) ids.push(r.id); });
189
+ }
190
+ return ids;
191
+ }
192
+
193
+ function extractHypothesisIds(payload) {
194
+ const ids = [];
195
+ if (payload?.hypothesisId) ids.push(payload.hypothesisId);
196
+ if (payload?.hypotheses && Array.isArray(payload.hypotheses)) {
197
+ payload.hypotheses.forEach((h) => { if (h && h.id) ids.push(h.id); });
198
+ }
199
+ return ids;
200
+ }
201
+
202
+ function extractExperimentIds(payload) {
203
+ const ids = [];
204
+ if (payload?.experimentId) ids.push(payload.experimentId);
205
+ if (payload?.experiments && Array.isArray(payload.experiments)) {
206
+ payload.experiments.forEach((e) => { if (e && e.id) ids.push(e.id); });
207
+ }
208
+ return ids;
209
+ }
210
+
211
+ function inferMandatoryIncluded(eventType) {
212
+ const normalized = eventType.toLowerCase();
213
+ if (normalized.includes("verification") || normalized.includes("decision") || normalized.includes("assessment")) return true;
214
+ return false;
215
+ }
216
+
217
+ function inferUnresolvedCriticalUnknowns(eventType, payload) {
218
+ const normalized = eventType.toLowerCase();
219
+ if (normalized.includes("invalidate") || normalized.includes("context")) {
220
+ if (payload?.unresolvedCriticalUnknowns) return payload.unresolvedCriticalUnknowns;
221
+ return [];
222
+ }
223
+ return [];
224
+ }