opencode-wait-a-minute 0.0.0-stage → 1.1.0-rc.8

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,19 @@
1
+ export function createSkillRegistry() {
2
+ const skills = {};
3
+ return {
4
+ register(name, skill) {
5
+ skills[name] = skill;
6
+ },
7
+ lookup(name) {
8
+ return skills[name];
9
+ },
10
+ getDeps(name) {
11
+ const skill = skills[name];
12
+ return skill && skill.deps ? skill.deps : [];
13
+ },
14
+ checkVersion(name, version) {
15
+ const skill = skills[name];
16
+ return skill ? skill.version === version : false;
17
+ },
18
+ };
19
+ }
@@ -0,0 +1,340 @@
1
+ /**
2
+ * Skill Routing Constraints — layer responsibility and dependency management.
3
+ *
4
+ * Ensures skills only modify their declared layer and respects dependencies.
5
+ * Prevents cross-layer contamination and skill conflicts.
6
+ */
7
+
8
+ /**
9
+ * @typedef {"backend" | "frontend" | "shared" | "infrastructure"} Layer
10
+ */
11
+
12
+ /**
13
+ * @typedef {Object} SkillConstraint
14
+ * @property {string} id - Skill ID
15
+ * @property {Layer[]} allowedLayers - Layers this skill can modify
16
+ * @property {string[]} dependsOn - Skills that must be selected first
17
+ * @property {string[]} conflicts - Skills that cannot coexist
18
+ * @property {string} reason - Why these constraints exist
19
+ */
20
+
21
+ /**
22
+ * @typedef {Object} RoutingDecision
23
+ * @property {string} skillId
24
+ * @property {boolean} allowed
25
+ * @property {Layer} targetLayer
26
+ * @property {string} reason
27
+ * @property {"constraint" | "dependency" | "conflict" | "budget"} reasonType
28
+ */
29
+
30
+ /** Default layer constraints for known skill types */
31
+ const DEFAULT_CONSTRAINTS = {
32
+ "angular-developer": {
33
+ allowedLayers: ["frontend"],
34
+ dependsOn: [],
35
+ conflicts: ["nestjs-best-practices"],
36
+ reason: "Angular skills should only modify frontend code",
37
+ },
38
+ "nestjs-best-practices": {
39
+ allowedLayers: ["backend"],
40
+ dependsOn: [],
41
+ conflicts: ["angular-developer"],
42
+ reason: "NestJS skills should only modify backend code",
43
+ },
44
+ "frontend-boundary-protection": {
45
+ allowedLayers: ["frontend"],
46
+ dependsOn: [],
47
+ conflicts: [],
48
+ reason: "Frontend protection should only modify frontend code",
49
+ },
50
+ "backend-integrity": {
51
+ allowedLayers: ["backend"],
52
+ dependsOn: [],
53
+ conflicts: [],
54
+ reason: "Backend integrity should only modify backend code",
55
+ },
56
+ "efficient-coding": {
57
+ allowedLayers: ["backend", "frontend", "shared"],
58
+ dependsOn: [],
59
+ conflicts: [],
60
+ reason: "General coding skill, can work on any layer",
61
+ },
62
+ "architectural-governance": {
63
+ allowedLayers: ["backend", "frontend", "shared", "infrastructure"],
64
+ dependsOn: [],
65
+ conflicts: [],
66
+ reason: "Governance skill, can review any layer",
67
+ },
68
+ // mattpocock/skills — MIT licensed, vendored into skills/
69
+ "writing-for-agents": {
70
+ allowedLayers: ["shared"],
71
+ dependsOn: [],
72
+ conflicts: [],
73
+ reason: "Meta-skill for writing agent documents and skills",
74
+ },
75
+ "codebase-design": {
76
+ allowedLayers: ["shared"],
77
+ dependsOn: [],
78
+ conflicts: [],
79
+ reason: "Deep module design vocabulary",
80
+ },
81
+ "diagnosing-bugs": {
82
+ allowedLayers: ["shared"],
83
+ dependsOn: [],
84
+ conflicts: [],
85
+ reason: "Bug diagnosis loop for hard bugs",
86
+ },
87
+ "tdd": {
88
+ allowedLayers: ["shared"],
89
+ dependsOn: [],
90
+ conflicts: [],
91
+ reason: "Test-driven development (TDD) reference",
92
+ },
93
+ "handoff": {
94
+ allowedLayers: ["shared"],
95
+ dependsOn: [],
96
+ conflicts: [],
97
+ reason: "Compact conversation for agent handoff",
98
+ },
99
+ "wizard": {
100
+ allowedLayers: ["shared"],
101
+ dependsOn: [],
102
+ conflicts: [],
103
+ reason: "Interactive bash wizard for manual tasks",
104
+ },
105
+ "prototype": {
106
+ allowedLayers: ["shared"],
107
+ dependsOn: [],
108
+ conflicts: [],
109
+ reason: "Throwaway prototype for design questions",
110
+ },
111
+ "improve-codebase-architecture": {
112
+ allowedLayers: ["shared"],
113
+ dependsOn: [],
114
+ conflicts: [],
115
+ reason: "Scan for deepening opportunities in architecture",
116
+ },
117
+ };
118
+
119
+ /**
120
+ * Detect which layer a file belongs to based on path patterns.
121
+ *
122
+ * @param {string} filePath
123
+ * @returns {Layer | null}
124
+ */
125
+ export function detectLayer(filePath) {
126
+ const lower = filePath.toLowerCase();
127
+
128
+ // Frontend patterns
129
+ if (
130
+ /\.(component|module|service|directive|pipe|html|css|scss|less)\.(ts|js)$/.test(lower) ||
131
+ /src[\\/]app[\\/]/.test(lower) ||
132
+ /src[\\/]components?[\\/]/.test(lower) ||
133
+ /src[\\/]pages?[\\/]/.test(lower) ||
134
+ /\.(jsx|tsx|vue|svelte)$/.test(lower)
135
+ ) {
136
+ return "frontend";
137
+ }
138
+
139
+ // Backend patterns
140
+ if (
141
+ /\.(controller|service|module|guard|interceptor|middleware|resolver)\.(ts|js)$/.test(lower) ||
142
+ /src[\\/](api|server|backend|nest)[\\/]/.test(lower) ||
143
+ /\.entity\.(ts|js)$/.test(lower) ||
144
+ /\.repository\.(ts|js)$/.test(lower) ||
145
+ /\.migration\.(ts|js)$/.test(lower)
146
+ ) {
147
+ return "backend";
148
+ }
149
+
150
+ // Infrastructure patterns
151
+ if (
152
+ /(dockerfile|docker-compose|\.yml|\.yaml)$/.test(lower) ||
153
+ /\b(infra|infrastructure|deploy|k8s|terraform)[\\/]/.test(lower) ||
154
+ /\.(tf|tfvars)$/.test(lower)
155
+ ) {
156
+ return "infrastructure";
157
+ }
158
+
159
+ // Shared patterns
160
+ if (
161
+ /\b(shared|common|utils?|helpers?|types?|constants?)[\\/]/.test(lower) ||
162
+ /\.(d\.ts|types\.ts)$/.test(lower)
163
+ ) {
164
+ return "shared";
165
+ }
166
+
167
+ return null;
168
+ }
169
+
170
+ /**
171
+ * Get constraints for a skill.
172
+ *
173
+ * @param {string} skillId
174
+ * @param {Object} skillRegistry - Skill registry (optional, for dynamic constraints)
175
+ * @returns {SkillConstraint}
176
+ */
177
+ export function getSkillConstraints(skillId, skillRegistry = {}) {
178
+ // Check default constraints first
179
+ if (DEFAULT_CONSTRAINTS[skillId]) {
180
+ return { id: skillId, ...DEFAULT_CONSTRAINTS[skillId] };
181
+ }
182
+
183
+ // Check skill registry for declared constraints
184
+ const skill = skillRegistry[skillId];
185
+ if (skill?.constraints) {
186
+ return {
187
+ id: skillId,
188
+ allowedLayers: skill.constraints.allowedLayers || ["backend", "frontend", "shared"],
189
+ dependsOn: skill.constraints.dependsOn || [],
190
+ conflicts: skill.constraints.conflicts || [],
191
+ reason: skill.constraints.reason || "No constraints declared",
192
+ };
193
+ }
194
+
195
+ // Default: allow all layers
196
+ return {
197
+ id: skillId,
198
+ allowedLayers: ["backend", "frontend", "shared", "infrastructure"],
199
+ dependsOn: [],
200
+ conflicts: [],
201
+ reason: "No constraints declared, allowing all layers",
202
+ };
203
+ }
204
+
205
+ /**
206
+ * Check if a skill can modify a specific layer.
207
+ *
208
+ * @param {string} skillId
209
+ * @param {Layer} targetLayer
210
+ * @param {Object} skillRegistry
211
+ * @returns {RoutingDecision}
212
+ */
213
+ export function canModifyLayer(skillId, targetLayer, skillRegistry = {}) {
214
+ const constraints = getSkillConstraints(skillId, skillRegistry);
215
+
216
+ if (constraints.allowedLayers.includes(targetLayer)) {
217
+ return {
218
+ skillId,
219
+ allowed: true,
220
+ targetLayer,
221
+ reason: `Skill ${skillId} is allowed to modify ${targetLayer}`,
222
+ reasonType: "constraint",
223
+ };
224
+ }
225
+
226
+ return {
227
+ skillId,
228
+ allowed: false,
229
+ targetLayer,
230
+ reason: `Skill ${skillId} is NOT allowed to modify ${targetLayer}. Allowed: ${constraints.allowedLayers.join(", ")}`,
231
+ reasonType: "constraint",
232
+ };
233
+ }
234
+
235
+ /**
236
+ * Check skill dependencies and conflicts.
237
+ *
238
+ * @param {string} skillId
239
+ * @param {string[]} selectedSkills - Currently selected skill IDs
240
+ * @param {Object} skillRegistry
241
+ * @returns {RoutingDecision[]}
242
+ */
243
+ export function checkDependencies(skillId, selectedSkills, skillRegistry = {}) {
244
+ const decisions = [];
245
+ const constraints = getSkillConstraints(skillId, skillRegistry);
246
+
247
+ // Check dependencies
248
+ for (const dep of constraints.dependsOn) {
249
+ if (!selectedSkills.includes(dep)) {
250
+ decisions.push({
251
+ skillId,
252
+ allowed: false,
253
+ targetLayer: "shared",
254
+ reason: `Skill ${skillId} requires ${dep} to be selected first`,
255
+ reasonType: "dependency",
256
+ });
257
+ }
258
+ }
259
+
260
+ // Check conflicts
261
+ for (const conflict of constraints.conflicts) {
262
+ if (selectedSkills.includes(conflict)) {
263
+ decisions.push({
264
+ skillId,
265
+ allowed: false,
266
+ targetLayer: "shared",
267
+ reason: `Skill ${skillId} conflicts with ${conflict}`,
268
+ reasonType: "conflict",
269
+ });
270
+ }
271
+ }
272
+
273
+ return decisions;
274
+ }
275
+
276
+ /**
277
+ * Route skills with constraints validation.
278
+ *
279
+ * @param {string[]} candidateSkills - Skills to route
280
+ * @param {string[]} targetFiles - Files to be modified
281
+ * @param {Object} skillRegistry
282
+ * @returns {Object} Routing result
283
+ */
284
+ export function routeWithConstraints(candidateSkills, targetFiles, skillRegistry = {}) {
285
+ const allowed = [];
286
+ const rejected = [];
287
+ const selectedLayers = new Set();
288
+
289
+ // Detect target layers
290
+ const targetLayers = new Set();
291
+ for (const file of targetFiles) {
292
+ const layer = detectLayer(file);
293
+ if (layer) targetLayers.add(layer);
294
+ }
295
+
296
+ // Check each candidate skill
297
+ for (const skillId of candidateSkills) {
298
+ let canRoute = true;
299
+ let rejectReason = "";
300
+
301
+ // Check layer constraints
302
+ for (const layer of targetLayers) {
303
+ const decision = canModifyLayer(skillId, layer, skillRegistry);
304
+ if (!decision.allowed) {
305
+ canRoute = false;
306
+ rejectReason = decision.reason;
307
+ break;
308
+ }
309
+ }
310
+
311
+ // Check dependencies and conflicts
312
+ const depDecisions = checkDependencies(skillId, allowed, skillRegistry);
313
+ const failedDeps = depDecisions.filter((d) => !d.allowed);
314
+ if (failedDeps.length > 0) {
315
+ canRoute = false;
316
+ rejectReason = failedDeps.map((d) => d.reason).join("; ");
317
+ }
318
+
319
+ if (canRoute) {
320
+ allowed.push(skillId);
321
+ for (const layer of targetLayers) {
322
+ selectedLayers.add(layer);
323
+ }
324
+ } else {
325
+ rejected.push({
326
+ skillId,
327
+ reason: rejectReason,
328
+ });
329
+ }
330
+ }
331
+
332
+ return {
333
+ allowed,
334
+ rejected,
335
+ targetLayers: Array.from(targetLayers),
336
+ selectedLayers: Array.from(selectedLayers),
337
+ };
338
+ }
339
+
340
+ export { DEFAULT_CONSTRAINTS };
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Lifecycle Manager — start/stop coordination with event listeners.
3
+ *
4
+ * Provides a tiny state machine coordinating ordered startup and shutdown
5
+ * of subsystems, emitting events for observability.
6
+ *
7
+ * States: "stopped" | "starting" | "running" | "stopping".
8
+ * Listeners can be attached per event.
9
+ *
10
+ * Designed for production hardening (M5-49): lets plugins register hooks
11
+ * that fire when the system transitions states.
12
+ */
13
+
14
+ /**
15
+ * @typedef {"stopped"|"starting"|"running"|"stopping"} LifecycleState
16
+ */
17
+
18
+ /**
19
+ * @typedef {Object} LifecycleListener
20
+ * @property {(event: {type: string, payload?: unknown}) => void} handler
21
+ */
22
+
23
+ /**
24
+ * @typedef {Object} LifecycleManager
25
+ * @property {() => LifecycleState} getState
26
+ * @property {() => Promise<void>} start
27
+ * @property {() => Promise<void>} stop
28
+ * @property {(event: string, handler: Function) => () => void} on
29
+ * @property {(event: string, handler: Function) => void} off
30
+ * @property {() => boolean} isRunning
31
+ */
32
+
33
+ /**
34
+ * Create a lifecycle manager.
35
+ * @param {Object} [opts]
36
+ * @param {Array<{name: string, onStart?: Function, onStop?: Function}>} [opts.components]
37
+ * @returns {LifecycleManager}
38
+ */
39
+ export function createLifecycleManager(opts = {}) {
40
+ const components = opts.components ?? [];
41
+ /** @type {LifecycleState} */
42
+ let state = "stopped";
43
+ /** @type {Map<string, Set<Function>>} */
44
+ const listeners = new Map();
45
+
46
+ function emit(type, payload) {
47
+ const set = listeners.get(type);
48
+ if (!set) return;
49
+ for (const fn of set) {
50
+ try {
51
+ fn({ type, payload });
52
+ } catch (_err) {
53
+ // listeners must not break lifecycle
54
+ }
55
+ }
56
+ }
57
+
58
+ function on(event, handler) {
59
+ if (typeof handler !== "function") throw new TypeError("handler must be function");
60
+ let set = listeners.get(event);
61
+ if (!set) {
62
+ set = new Set();
63
+ listeners.set(event, set);
64
+ }
65
+ set.add(handler);
66
+ return () => off(event, handler);
67
+ }
68
+
69
+ function off(event, handler) {
70
+ const set = listeners.get(event);
71
+ if (!set) return;
72
+ set.delete(handler);
73
+ if (set.size === 0) listeners.delete(event);
74
+ }
75
+
76
+ async function start() {
77
+ if (state === "running" || state === "starting") return;
78
+ state = "starting";
79
+ emit("state", { from: "stopped", to: "starting" });
80
+ for (const comp of components) {
81
+ emit("start:component", { name: comp.name });
82
+ if (typeof comp.onStart === "function") {
83
+ await comp.onStart();
84
+ }
85
+ }
86
+ state = "running";
87
+ emit("state", { from: "starting", to: "running" });
88
+ }
89
+
90
+ async function stop() {
91
+ if (state === "stopped" || state === "stopping") return;
92
+ state = "stopping";
93
+ emit("state", { from: "running", to: "stopping" });
94
+ // stop in reverse order
95
+ for (const comp of [...components].reverse()) {
96
+ emit("stop:component", { name: comp.name });
97
+ if (typeof comp.onStop === "function") {
98
+ await comp.onStop();
99
+ }
100
+ }
101
+ state = "stopped";
102
+ emit("state", { from: "stopping", to: "stopped" });
103
+ }
104
+
105
+ return {
106
+ getState: () => state,
107
+ isRunning: () => state === "running",
108
+ start,
109
+ stop,
110
+ on,
111
+ off,
112
+ };
113
+ }
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Context State Machine — derives operational state from isolated states.
3
+ *
4
+ * Connects Task lifecycle, Run lifecycle, Evidence lifecycle,
5
+ * Context routing, and Verification into a single derived state.
6
+ *
7
+ * The operational state is DERIVED, not persisted.
8
+ * It answers: "What can the agent do right now?"
9
+ */
10
+
11
+ /**
12
+ * @typedef {"PROPOSED" | "ASKING" | "IMPLEMENTING" | "VERIFYING" | "COMPLETE" | "BLOCKED" | "CONFLICTED" | "DEGRADED"} OperationalState
13
+ */
14
+
15
+ /**
16
+ * @typedef {Object} StateInputs
17
+ * @property {string} executionState - Current task execution state
18
+ * @property {string} routingStatus - Context router status
19
+ * @property {string} evidenceStatus - Evidence validity status
20
+ * @property {string} verificationStatus - Verification lifecycle status
21
+ * @property {boolean} hasActiveRun - Whether there's an active run
22
+ * @property {boolean} hasUnresolvedAssumptions - Whether there are blocking assumptions
23
+ */
24
+
25
+ /** All valid operational states */
26
+ export const OPERATIONAL_STATES = new Set([
27
+ "PROPOSED",
28
+ "ASKING",
29
+ "IMPLEMENTING",
30
+ "VERIFYING",
31
+ "COMPLETE",
32
+ "BLOCKED",
33
+ "CONFLICTED",
34
+ "DEGRADED",
35
+ ]);
36
+
37
+ /**
38
+ * Derive operational state from multiple state inputs.
39
+ *
40
+ * @param {StateInputs} inputs
41
+ * @returns {{ state: OperationalState, reason: string, blockers: string[] }}
42
+ */
43
+ export function deriveOperationalState(inputs) {
44
+ const {
45
+ executionState = "INITIALIZING",
46
+ routingStatus = "COMPLETE",
47
+ evidenceStatus = "valid",
48
+ verificationStatus = "UNVERIFIED",
49
+ hasActiveRun = false,
50
+ hasUnresolvedAssumptions = false,
51
+ } = inputs;
52
+
53
+ const blockers = [];
54
+
55
+ // Terminal: COMPLETED → DONE
56
+ if (executionState === "COMPLETED") {
57
+ return {
58
+ state: "COMPLETE",
59
+ reason: "task completed",
60
+ blockers: [],
61
+ };
62
+ }
63
+
64
+ // CONFLICTED: routing has conflicts (check before BLOCKED)
65
+ if (routingStatus === "CONFLICTED") {
66
+ return {
67
+ state: "CONFLICTED",
68
+ reason: "context conflicts detected",
69
+ blockers: ["context conflicts unresolved"],
70
+ };
71
+ }
72
+
73
+ // ASKING: waiting for authorization
74
+ if (executionState === "WAITING_AUTHORIZATION") {
75
+ return {
76
+ state: "ASKING",
77
+ reason: "waiting for authorization",
78
+ blockers: [],
79
+ };
80
+ }
81
+
82
+ // BLOCKED: multiple conditions
83
+ if (executionState === "FAILED") {
84
+ blockers.push("execution failed");
85
+ }
86
+ if (evidenceStatus === "invalidated") {
87
+ blockers.push("evidence invalidated");
88
+ }
89
+ if (hasUnresolvedAssumptions) {
90
+ blockers.push("unresolved decision-critical assumptions");
91
+ }
92
+ if (executionState === "BLOCKED") {
93
+ blockers.push("execution blocked");
94
+ }
95
+
96
+ if (blockers.length > 0) {
97
+ return {
98
+ state: "BLOCKED",
99
+ reason: blockers.join("; "),
100
+ blockers,
101
+ };
102
+ }
103
+
104
+ // VERIFYING: in verification phase
105
+ if (executionState === "VERIFYING" || verificationStatus === "VERIFYING") {
106
+ return {
107
+ state: "VERIFYING",
108
+ reason: "verification in progress",
109
+ blockers: [],
110
+ };
111
+ }
112
+
113
+ // DEGRADED: working but with issues
114
+ if (routingStatus === "PARTIAL") {
115
+ return {
116
+ state: "DEGRADED",
117
+ reason: "partial context available",
118
+ blockers: [],
119
+ };
120
+ }
121
+ if (evidenceStatus === "superseded") {
122
+ return {
123
+ state: "DEGRADED",
124
+ reason: "evidence superseded",
125
+ blockers: [],
126
+ };
127
+ }
128
+
129
+ // IMPLEMENTING: actively executing
130
+ if (executionState === "EXECUTING" || hasActiveRun) {
131
+ return {
132
+ state: "IMPLEMENTING",
133
+ reason: "execution in progress",
134
+ blockers: [],
135
+ };
136
+ }
137
+
138
+ // PROPOSED: initial state
139
+ return {
140
+ state: "PROPOSED",
141
+ reason: "task proposed, not yet started",
142
+ blockers: [],
143
+ };
144
+ }
145
+
146
+ /**
147
+ * Check if an action is allowed in the current operational state.
148
+ *
149
+ * @param {OperationalState} state
150
+ * @param {string} action
151
+ * @returns {{ allowed: boolean, reason: string }}
152
+ */
153
+ export function canPerformAction(state, action) {
154
+ const allowed = {
155
+ PROPOSED: ["start", "plan", "ask"],
156
+ ASKING: ["respond", "cancel"],
157
+ IMPLEMENTING: ["execute", "observe", "evidence", "pause"],
158
+ VERIFYING: ["verify", "evidence", "fail"],
159
+ COMPLETE: [],
160
+ BLOCKED: ["resolve", "cancel"],
161
+ CONFLICTED: ["resolve", "cancel"],
162
+ DEGRADED: ["execute", "observe", "evidence", "pause"],
163
+ };
164
+
165
+ const actions = allowed[state] || [];
166
+ if (actions.includes(action)) {
167
+ return { allowed: true, reason: `${action} allowed in ${state}` };
168
+ }
169
+
170
+ return {
171
+ allowed: false,
172
+ reason: `${action} not allowed in ${state}. Allowed: ${actions.join(", ") || "none"}`,
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Generate operational state report.
178
+ *
179
+ * @param {StateInputs} inputs
180
+ * @returns {Object} Report with state, reason, blockers, allowed actions
181
+ */
182
+ export function generateOperationalReport(inputs) {
183
+ const { state, reason, blockers } = deriveOperationalState(inputs);
184
+
185
+ const allActions = ["start", "plan", "ask", "respond", "cancel", "execute", "observe", "evidence", "pause", "verify", "fail", "resolve"];
186
+ const allowedActions = allActions.filter((a) => canPerformAction(state, a).allowed);
187
+
188
+ return {
189
+ state,
190
+ reason,
191
+ blockers,
192
+ allowedActions,
193
+ inputs,
194
+ derivedAt: Date.now(),
195
+ };
196
+ }
197
+
198
+ export default deriveOperationalState;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Projection Engine — derive state from an event stream via reducer.
3
+ *
4
+ * Given a reducer function and initial state, applies events one-by-one
5
+ * to produce a current derived state. Production hardening (M5-52).
6
+ */
7
+
8
+ /**
9
+ * @typedef {Object} Projection
10
+ * @property {*} getState
11
+ * @property {(event: {type: string, payload?: unknown}) => *} apply
12
+ * @property {() => number} version
13
+ * @property {() => void} reset
14
+ */
15
+
16
+ /**
17
+ * Create a projection from a reducer.
18
+ * @param {(state: *, event: {type: string, payload?: unknown}) => *} reducer
19
+ * @param {*} initialState
20
+ * @returns {Projection}
21
+ */
22
+ export function createProjection(reducer, initialState) {
23
+ if (typeof reducer !== "function") throw new TypeError("reducer must be function");
24
+ let state = initialState;
25
+ let v = 0;
26
+
27
+ function apply(event) {
28
+ state = reducer(state, event);
29
+ v++;
30
+ return state;
31
+ }
32
+
33
+ function getState() {
34
+ return state;
35
+ }
36
+
37
+ function version() {
38
+ return v;
39
+ }
40
+
41
+ function reset() {
42
+ state = initialState;
43
+ v = 0;
44
+ }
45
+
46
+ return { getState, apply, version, reset };
47
+ }