opencode-wait-a-minute 0.0.0-stage → 1.1.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +318 -2
- package/index.js +1836 -0
- package/package.json +70 -4
- package/skills/codebase-design/SKILL.md +114 -0
- package/skills/design-contract/SKILL.md +27 -0
- package/skills/design-critique/SKILL.md +19 -0
- package/skills/design-system/SKILL.md +17 -0
- package/skills/design-system/references/image-analysis.md +14 -0
- package/skills/design-system/references/tokens.md +32 -0
- package/skills/design-system/references/ui-rules.md +14 -0
- package/skills/diagnosing-bugs/SKILL.md +138 -0
- package/skills/frontend-design/SKILL.md +36 -0
- package/skills/handoff/SKILL.md +16 -0
- package/skills/improve-codebase-architecture/SKILL.md +71 -0
- package/skills/linter-negative/SKILL.md +74 -0
- package/skills/no-slop-ui/SKILL.md +26 -0
- package/skills/prototype/SKILL.md +26 -0
- package/skills/registry.json +44087 -0
- package/skills/tastemaker/SKILL.md +8 -0
- package/skills/tastemaker/references/tastemaker.md +14 -0
- package/skills/tdd/SKILL.md +38 -0
- package/skills/visual-review/SKILL.md +103 -0
- package/skills/visual-review/references/review-gates.md +42 -0
- package/skills/visual-review/references/surface-archetypes.md +37 -0
- package/skills/wizard/SKILL.md +44 -0
- package/skills/writing-for-agents/SKILL.md +81 -0
- package/src/cognition/cognition-store.js +251 -0
- package/src/cognition/cognitive-state.js +126 -0
- package/src/cognition/hypothesis-manager.js +113 -0
- package/src/cognition/observation-engine.js +112 -0
- package/src/cognition/observation-provenance.js +268 -0
- package/src/context/active-context-boundary.js +68 -0
- package/src/context/assembly.js +494 -0
- package/src/context/assessment-rules.js +55 -0
- package/src/context/capsule-staleness.js +267 -0
- package/src/context/capsules.js +2 -0
- package/src/context/compare-array.js +98 -0
- package/src/context/compare-object.js +89 -0
- package/src/context/compare-values.js +42 -0
- package/src/context/context-assembly-contract.js +71 -0
- package/src/context/context-budget-manager.js +232 -0
- package/src/context/context-builder.js +49 -0
- package/src/context/context-cache.js +55 -0
- package/src/context/context-capture.js +243 -0
- package/src/context/context-classification.js +199 -0
- package/src/context/context-compaction.js +179 -0
- package/src/context/context-decision-audit.js +267 -0
- package/src/context/context-demotion.js +173 -0
- package/src/context/context-dictionary.js +106 -0
- package/src/context/context-drift-detection.js +225 -0
- package/src/context/context-evaluation.js +374 -0
- package/src/context/context-event-ingress.js +100 -0
- package/src/context/context-event-normalization.js +224 -0
- package/src/context/context-feedback-loop.js +216 -0
- package/src/context/context-freshness.js +21 -0
- package/src/context/context-graph-builder.js +71 -0
- package/src/context/context-graph.js +451 -0
- package/src/context/context-history-relevance.js +321 -0
- package/src/context/context-interception.js +145 -0
- package/src/context/context-invalidation-events.js +154 -0
- package/src/context/context-lifecycle.js +170 -0
- package/src/context/context-loop-prevention.js +347 -0
- package/src/context/context-manager.js +340 -0
- package/src/context/context-memory-layers.js +265 -0
- package/src/context/context-minimality.js +166 -0
- package/src/context/context-normalization.js +130 -0
- package/src/context/context-optimization-metrics.js +434 -0
- package/src/context/context-output-policy.js +135 -0
- package/src/context/context-pack-authority.js +157 -0
- package/src/context/context-promotion.js +172 -0
- package/src/context/context-query-contract.js +101 -0
- package/src/context/context-ranking.js +164 -0
- package/src/context/context-rate-distortion.js +261 -0
- package/src/context/context-recovery.js +209 -0
- package/src/context/context-retrieval-integration.js +60 -0
- package/src/context/context-retrieval.js +93 -0
- package/src/context/context-router.js +457 -0
- package/src/context/context-routing-evaluation.js +331 -0
- package/src/context/context-routing-observability.js +296 -0
- package/src/context/context-routing-shadow.js +237 -0
- package/src/context/context-scope.js +181 -0
- package/src/context/context-snapshot.js +226 -0
- package/src/context/context-source-registry.js +95 -0
- package/src/context/context-sufficiency-gate.js +120 -0
- package/src/context/context-sufficiency-oracle.js +167 -0
- package/src/context/context-telemetry.js +176 -0
- package/src/context/context.js +633 -0
- package/src/context/dependency-closure.js +246 -0
- package/src/context/page-fault-tracker.js +275 -0
- package/src/context/prompt-engine.js +74 -0
- package/src/context/runtime-context-graph.js +260 -0
- package/src/context/selection.js +2 -0
- package/src/context/session.js +2 -0
- package/src/context/sufficiency-contract.js +300 -0
- package/src/context/tokenizer.js +17 -0
- package/src/context-benchmark-router.mjs +125 -0
- package/src/evidence/evidence-engine.js +161 -0
- package/src/evidence/evidence-factory.js +3 -0
- package/src/evidence/evidence-freshness.js +463 -0
- package/src/evidence/evidence-gap.js +76 -0
- package/src/evidence/evidence-lineage.js +525 -0
- package/src/evidence/evidence.js +136 -0
- package/src/evidence/freshness-validation.js +187 -0
- package/src/evidence/lineage.js +4 -0
- package/src/execution/action-evaluation.js +179 -0
- package/src/execution/circuit-breaker.js +70 -0
- package/src/execution/claim-interception.js +63 -0
- package/src/execution/execution-engine.js +258 -0
- package/src/execution/execution-intent.js +69 -0
- package/src/execution/execution-state.js +89 -0
- package/src/execution/formatting.js +60 -0
- package/src/execution/l1-sanitize.js +16 -0
- package/src/execution/l2-resource-guards.js +19 -0
- package/src/execution/l3-invariant-checker.js +5 -0
- package/src/execution/l4-containment.js +6 -0
- package/src/execution/runtime-guards.js +106 -0
- package/src/execution/strategy-identity.js +162 -0
- package/src/execution/tool-interceptor.js +7 -0
- package/src/execution/tool-result-classifier.js +63 -0
- package/src/integration/canary-deploy.js +143 -0
- package/src/integration/collector.js +59 -0
- package/src/integration/error-recovery.js +6 -0
- package/src/integration/event-bus.js +66 -0
- package/src/integration/health-checker.js +57 -0
- package/src/integration/logger.js +39 -0
- package/src/integration/message-handler.js +611 -0
- package/src/integration/opencode-db.js +153 -0
- package/src/integration/orchestration.js +419 -0
- package/src/integration/part-provenance.js +65 -0
- package/src/integration/rate-limiter.js +55 -0
- package/src/integration/release-gate.js +336 -0
- package/src/integration/rollback-manager.js +164 -0
- package/src/integration/router-adapter.js +269 -0
- package/src/integration/telemetry.js +45 -0
- package/src/integration/update-checker.js +10 -0
- package/src/persistence/event-sourcing.js +75 -0
- package/src/persistence/memory.js +501 -0
- package/src/persistence/migration-runner.js +16 -0
- package/src/persistence/persistence-manager.js +39 -0
- package/src/persistence/transaction-log.js +157 -0
- package/src/policy/action-capabilities.js +116 -0
- package/src/policy/action-policy.js +4 -0
- package/src/policy/assessment-engine.js +104 -0
- package/src/policy/assumption-tracking.js +66 -0
- package/src/policy/budget-enforcer.js +7 -0
- package/src/policy/config-validator.js +102 -0
- package/src/policy/execution-assessment.js +142 -0
- package/src/policy/failure-classification.js +5 -0
- package/src/policy/feature-flags.js +65 -0
- package/src/policy/governance-enforcement.js +118 -0
- package/src/policy/locale.js +15 -0
- package/src/policy/multi-signer.js +151 -0
- package/src/policy/policy-state-machine.js +97 -0
- package/src/policy/ponytail-constraint.js +38 -0
- package/src/policy/project-inspector.js +93 -0
- package/src/policy/query-builder.js +14 -0
- package/src/policy/request-classifier.js +79 -0
- package/src/policy/risk-engine.js +266 -0
- package/src/policy/scope-enforcement.js +140 -0
- package/src/policy/secret-scanner.js +68 -0
- package/src/policy/skill-routing.js +104 -0
- package/src/policy/strategy-capabilities.js +211 -0
- package/src/policy/strategy.js +6 -0
- package/src/policy/uncertainty.js +117 -0
- package/src/shared/container.js +44 -0
- package/src/shared/decision-types.js +24 -0
- package/src/shared/simple-test.js +160 -0
- package/src/shared/wam-events.js +52 -0
- package/src/skills/engine.js +1705 -0
- package/src/skills/skill-registry.js +19 -0
- package/src/skills/skill-routing.js +340 -0
- package/src/state/lifecycle-manager.js +113 -0
- package/src/state/operational-state.js +198 -0
- package/src/state/projections.js +47 -0
- package/src/state/replay-engine.js +5 -0
- package/src/state/requirement-ownership.js +20 -0
- package/src/state/requirement-state.js +152 -0
- package/src/state/run-event-identity.js +172 -0
- package/src/state/state-engine.js +19 -0
- package/src/state/state-machine.js +119 -0
- package/src/state/state-schema.js +318 -0
- package/src/state/state-store.js +335 -0
- package/src/state/state-transition.js +84 -0
- package/src/state/task-dependencies.js +287 -0
- package/src/state/task-execution.js +217 -0
- package/src/state/task-lifecycle.js +59 -0
- package/src/state/task-runs.js +361 -0
- package/src/state/task-state-file.js +86 -0
- package/src/state/task-state.js +41 -0
- package/src/state/task-status.js +54 -0
- package/src/state/task-store.js +48 -0
- package/src/state/wam-state.js +148 -0
- package/src/verification/completion-gate-simple.js +8 -0
- package/src/verification/completion-gate.js +183 -0
- package/src/verification/contract.js +9 -0
- package/src/verification/contradiction-resolution.js +255 -0
- package/src/verification/diagnostic-engine.js +659 -0
- package/src/verification/false-completion-prevention.js +100 -0
- package/src/verification/hook-validator.js +6 -0
- package/src/verification/requirements.js +6 -0
- package/src/verification/verification-context.js +162 -0
- package/src/verification/verification-lifecycle.js +66 -0
- package/src/verification/verification-model.js +89 -0
- package/src/verification/verification-persistence.js +129 -0
- package/src/verification/verification-policy.js +127 -0
- package/src/verification/verification.js +415 -0
|
@@ -0,0 +1,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
|
+
}
|